NoteVersion : 1.0、
TestVersion : ts2.4.2
Data : 2017年8月27日
l 理解ES5、ES6、javaScript、TypeScript的概念和關系
l JavaScript開發經驗
l 如果懂java學習來會事半功倍
u 支持ES6規范
u 強大的IDE支持
u Angluar2的開發語言
Visual Studio Code 工具開發
3、2、1、安裝node.js
安裝文件下載地址:Node.js Downloads。TypeScript源碼需要進行編譯以后才能運行,Node.js提供了編譯環境。
3、2、2、安裝TypeScript編譯工具
安裝好node.js后,
Nodejs安裝: http://www.runoob.com/nodejs/nodejs-install-setup.html |
cmd,輸入以下命令
npm install -g typescript |
使用npm包管理工具下載TypeScript包并在全局環境下安裝,安裝成功后就可以通過
tsc 命令編譯TypeScript源碼。可以通過tsc -v 命令查看當前TypeScript版本。當前最新版本是:1.8
檢查TypeScript版本
3、3、3、使用visial studio code進行開發(待完善)
一下目錄結構是一個簡單的demo的結構
模板字符串由 : 反撇號(` `)
1、支持內部換行、插值、嵌套(ES6說可以嵌套,目前未測試出)
let html = `<div> < p>支持換行功能</p> </div`; let myname = 'liangjiaming'; console.log(`${myname}插值功能${html}, 轉移展示:土豪有很多$\`\\{轉移展示結束 `); 控制臺結果:
|
Tip: 需要在模板字符串中引入字符$和{ 用反斜杠轉義每一個字符:`$`和`\{`
2、自動拆分字符串
function print(temple, name, age) { console.log(`temple:${temple}`); console.log(`name:${name}`); console.log(`age:${age}`); } let myName = "liangjiaming"; function getAge() { return 18; } print`hello my name is ${myName},i'm ${getAge()}`; // 表達式1 print `hello my name is ${myName},i'm `; // 表達式2報錯 但解析后仍然能打印出信息 print`hello my name is ${myName},i'm ${getAge()},${}`; // 表達式3 報錯 控制臺結果:
|
小結:字符串模板是支持模板內部換行以及插值(${})
使用字符串模板調用方法的時候,會進行自動的拆分,傳遞參數用${},并且ts會進行參數個數的校驗,若參數個數符合則正常拆分,若不符合個數則進行順序賦值。
4.2.1參數類型
ES6中原始類型:
l ? Undefined 未定義
l ? Null 空值
l ? Boolean 布爾類型
l ? Number 數字類型
l ? String 字符串類型
l ? Object 對象類型
TS中的數據類型
n Boolean 布爾
n Number 數字
n String 字符串
n Array 數組
n Enum 枚舉
n Any 任意類型
n Void 一般這種類型都是用在函數的返回值
// 聲明變量類型 let myname: string = `jimmy`; myname = 13 // 編譯報錯 let alias = 'jimmy'; alias = 14; // 編譯報錯 let eve: any = 'jimmy'; eve = 10; let age: number = 11; // 方法參數類型、返回值類型 function test(name: string): string { return name; } |
4.2.2參數默認值
聲明默認值、方法參數默認值
var myname: string = `jimmy`; // 默認值參數要聲明在必選參數之后 function test(a: string, b: string, c: string = 'yoyo') { console.log(`a:${a},b:${b},c:${c}`) } test(myname, 'Hi'); test(myname); // 報錯 控制臺打印結果
|
4.2.3 可選參數
var myname: string = `jimmy`; // 可選參數必須聲明在必選參數后面 function test(a: string, b?: string, c: string = 'yoyo') { console.log(`a:${a},b:${b},c:${c}`) // 需要單獨處理不傳的時候的報錯異常ERROR console.log(b.length); } test(myname, 'Hi'); test(myname);
|
4.3.1 Rest and Spread[…]操作符(ES6不定參數)
用來聲明任意數量的方法參數
// 聲明不定參數 function test(a, ...needle ) { console.log(`a:${a}`); console.log(`needle:${needle}`) } test('jimmy','love', 'mother'); let para = ['jimmy','love', 'mother']; test(para);控制臺結果:
|
ES6還支持一下調用方式,TS目前不支持這種語法
function test(a, b, c) { console.log(`a:${a} b:${b} c:${c}`) } var para = ['jimmy', 'love', 'daddy']; var args = ['jimmy', 'deepLove', 'daddy', 'mother', 'lover']; test(...para); test(...args); 控制臺結果:
|
4.3.2 generator函數(ES6 生成器generators)
控制函數的執行,手工暫定和恢復代碼執行。
關鍵字:yield
function* doSomething(){ yield console.log("start"); yield console.log("Hi! I am jimmy"); yield console.log('finish'); } let iter = doSomething(); 控制臺結果:
|
代碼片段2:
function* getStockPrice(stock) { while (true) { yield Math.random()*100; } } var priceGenerator = getStockPrice('IBM'); let limitPrice = 30; var price = 100; while (price > limitPrice) { price = priceGenerator.next().value; console.log(`price:${price}`); } console.log(`buy IBM at ${price}`); 控制臺結果:
|
4.3.3理解generator
首先看如下代碼:
function* doSomething(){ yield Math.random()*100; yield Math.random()*100; yield Math.random()*100; } let iter = doSomething(); 控制臺結果:
|
運行iter.next()方法的時候查看返回值
{value: 90.16063004650843, done: false}
done : false
value : 90.16063004650843
__proto__:Object
value為yield后面表達是(語句塊)返回值。
done為generator的是否存在下步false表示有 true表示沒有
ES6:調用generators(ES6概念生成器)的時候,它不是立即執行,而是返回一個已暫停的生成器對象, 當調用生成器對象的.next()方法時,函數調用將其自身解凍并一直運行到下一個 yield(若有java基礎可以理解為java的阻塞) 表達式,再次暫停。調用最后一個 iter.next()時,我們最終抵達生成器函數的末尾,所以返回結果中 done的值為 true。抵達函數的末尾意味著沒有返回值,所以返回結果中 value 的值為undefined Tip:生成器不是線程 當生成器運行時,它和調用者處于同一線程中,擁有確定的連續執行順序,永不并發 生成器就是迭代器! |
擴展思考:ES6所提及的promise編程方式
例子1:
function getStock() { return { code : 'IBM', price: 100, contact:{ phone: 13100000000, tel : '0752-7895642' } } } let { code, price,contact } = getStock(); console.log(`code:${code} price:${price} contact:${contact}`) 控制臺結果:
|
解析:數組、對象、其他
數組:
let [a, b, c] = [1, 2, 3]; let arr1 = ['jimmy', 'see', 'Lily', 'hit', 'Tom']; let [myName, opt, ...para] = arr1; console.log(`${myName}${opt}${para}`)控制臺結果:
|
對象:
let obj = { a: 1, b: 2, c: 3, d: 4, arr: [ 'Yo.', { sone:'typeScript' } ] } let { a, b: B } = obj; console.log(`a:${a} B:${B}`); let c = 0; ({ c, d,e=1 } = obj); // ts編譯報錯 console.log(`c:${c} d:$z1lj3h1 e:${e}`); let { arr: [greeting, { sone }] } = obj; // ts編譯報錯 console.log(`${greeting} ${sone}`); 控制臺結果:
|
方法:
let { floor, pow } = Math; console.log(floor(1.9)); console.log(pow(2,3)); 控制臺結果:
|
其他:
let { length } = 'Yo.'; console.log(length) let [ a,b,c ]= 'Yo.'; console.log(`${a} ${b} ${c}`) 控制臺結果:
|
let myArray = [1, 2, 3, 4, 5]; console.log(myArray.filter(value => value % 2 == 0)); 控制臺結果:
|
解放匿名函數中this關鍵字的問題
function getStock(name:string) { this.name = name; setInterval(function () { console.log("ES5"+this.name); },1000); } var stock = getStock('IBM'); function getStock2(name:string) { this.name = name; setInterval( () =>{ console.log("ES6"+this.name); }); } var stock2 = getStock('IBM'); 控制臺結果: |
var myArray = [10, 20, 30, 40]; myArray.desc='ES5寫法,ts不要這么寫' // forEach 循環 myArray.forEach(value => console.log("forEach循環:"+value)); for (var v in myArray) { console.log("for in循環:"+ v); } for (var a of myArray) { console.log("for of循環:" + v); } 控制臺結果: |
Java程序員的福音
知識點:
類的定義、構造、屬性、方法
訪問控制符(public private protected)
繼承(extends super)
4.7.1定義、構造、屬性、方法:
class Person{ private age; protected sex; constructor(public myName :string) { console.log("hi"); this.eat(); } eat() { console.log(`${this.myName} is eating`); } work() { this.eat(); console.log("then working"); } } let p = new Person(`jimmy`); 控制臺結果:
|
4.7.2訪問控制符(public private protected)
Public 類內部和外部均可以訪問
Protected 類內部以及其子類均可以訪問
Private 只有類內部可以訪問
4.7.3繼承
class Person{ private age = 18; protected sex = '男'; constructor(public myName: string) { //this.sex = '男'; console.log("hi"); this.eat(); } eat() { console.log(`${this.myName} is eating`); } protected work() { this.eat(); console.log("then working"); } } class Emplyee extends Person{ } let e = new Emplyee("Arui"); console.log(e.sex); // ts報錯 控制臺結果:
|
Tip:繼承不會繼承private的屬性和方法,子類的構造方法必須調用父類的構造方法。
Super的理解只能調用父類的方法(包括構造方法和普通方法),TS不支持多繼承
class Person{ private age = 18; protected sex = '男'; constructor(public myName: string) { //this.sex = '男'; console.log("hi"); this.eat(); } eat() { console.log(`${this.myName} is eating`); } protected work() { this.eat(); console.log("then working"); } } class Emplyee extends Person{ constructor(public myName:string,public hobby:string) { super(myName); console.log(`${this.myName}性別${this.sex}喜歡${this.hobby}`); } } let e = new Emplyee("Arui","女"); 控制臺結果:
|
采用java的概念解釋即為參數化類型
/** * 沒有泛型,我們要么必須給身份功能的特定類型 */ function identity1(arg: number): number { return arg; } /** * 或者:我們可以描述使用“任意”類型的標識功能: */ function identity2(arg: any): any { return arg; } console.log(identity1('jimmy')); // ts報錯 console.log(identity2('jimmy')); 控制臺結果:
|
用法一: 作為方法參數的約束
interface IPerson{ name: string; age: number; } class Person{ constructor(public config: IPerson) { console.log(this.config); } } var p = new Person({ name: 'jimmy', age:18 }) var p = new Person({ name: 50, // ts報錯 age1:18 // ts報錯 }) 控制臺結果:
|
用法二:類似java的接口使用,定義一些抽象方法,實現類中必須實現。甚至原則都一樣,接口中的屬性和方法必須是public的。
interface IPerson{ private age: number; // ts 報錯 protected sex: string; // ts 報錯 eat(); // 無實現 private sing(); // ts 報錯 } class Man implements IPerson{ eat() { console.log(`吃的多`) } } class Women implements IPerson{ // 未實現接口方法則ts報錯 } let m = new Man(); m.eat();控制臺結果: |
關鍵字:export import
類似于java的包概念,但略有不同
1、export 可以選擇對外暴露哪些屬性和方法
Import 引用其他模塊的屬性或者方法
ES6補充:
Export列表
不需要標記每一個被導出的特性,你只需要在花括號中按照列表的格式寫下你想
導出的所有名稱
export{detectCats, Kittydar};
// 此處不需要`export`關鍵字
function detectCats(canvas,options) { ... }
classKittydar { ... }
重命名 import 和 和 export
// 這兩個模塊都會導出以`flip`命名的東西。 // 要同時導入兩者,我們至少要將其中一個的名稱改掉。 import {flip as flipOmelet} from "eggs.js"; import {flip as flipHouse} from "real-estate.js"; |
// unlicensed_nuclear_accelerator.js - 無 DRM(數字版權管理)的媒體流 // (這不是一個真實存在的庫,但是或許它應該被做成一個庫) function v1() { ... } function v2() { ... } export { v1 as streamV1, v2 as streamV2, v2 as streamLatestVersion }; |
Angular2學習中在進行詳細學習
在TS中使用JS的第三方的框架如jqeury等。
類型定義文件 (*.d.ts)
一、TS的環境搭建 需要安裝nodeJS 使用npm命令在線安裝
二、TS的特性
對ES6大部分的新特性進行了支持和擴展、借鑒了一些java的特性
1、 字符串模板(``)
2、 參數默認值、可選參數、不定參數(參數類型)
3、 生成器、結構、箭頭函數/表達式
4、 類(class)
5、 for..of 循環
6、 繼承
7、 接口
8、 泛型
9、 模塊、注解
Tip:本筆記初學者查看可以了解個大致的概念,寫得不是很詳細。大神若看到了。還請指正
Tip_2:建議瀏覽一遍ES6的基本特性在學習TS若有java基礎學習TS相對會簡單。
封面圖(侵權刪)
說今年最熱門的前端技術,Vue3 和 TS 絕對榜上有名了。據了解,已經有很多公司在使用 Vue3 + TS + Vite 開發新項目了。那么我們也不能落后,今天就給大家分享一下如何在 Vue3 組件中結合 Composition-Api 使用 TS 類型。如果有不會或者不熟的小伙伴,一起學起來吧!
使用 <script setup>
當使用 <script setup> 時,defineProps() 宏函數支持從它的參數中推導類型:
<script setup lang="ts">
const props = defineProps({
foo: { type: String, required: true },
bar: Number
})
props.foo // string
props.bar // number | undefined
</script>
這被稱為 運行時聲明 ,因為傳遞給 defineProps() 的參數會作為運行時的 props 選項使用。
第二種方式,通過泛型參數來定義 props 的類型,這種方式更加直接:
<script setup lang="ts">
const props = defineProps<{
foo: string
bar?: number
}>()
</script>
// or
<script setup lang="ts">
interface Props {
foo: string
bar?: number
}
const props = defineProps<Props>()
</script>
這被稱為 基于類型的聲明 ,編譯器會盡可能地嘗試根據類型參數推導出等價的運行時選項。 這種方式的不足之處在于,失去了定義 props 默認值的能力。為了解決這個問題,我們可以使用 withDefaults 編譯器宏:
interface Props {
msg?: string
labels?: string[]
}
const props = withDefaults(defineProps<Props>(), {
msg: 'hello',
labels: () => ['one', 'two']
})
上面代碼會被編譯為等價的運行時 props 的 default 選項。
非 <script setup>
如果沒有使用 <script setup>,那么為了開啟 props 的類型推導,必須使用 defineComponent()。傳入 setup() 的 props 對象類型是從 props 選項中推導而來。
import { defineComponent } from 'vue'
export default defineComponent({
props: {
message: String
},
setup(props) {
props.message // <-- 類型:string
}
})
使用 <script setup>
在 <script setup> 中,emit 函數的類型標注也可以使用 運行時聲明 或者 基于類型的聲明 :
<script setup lang="ts">
// 運行時
const emit = defineEmits(['change', 'update'])
// 基于類型
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
</script>
我們可以看到,基于類型的聲明 可以使我們對所觸發事件的類型進行更細粒度的控制。
非 <script setup>
若沒有使用 <script setup>,defineComponent() 也可以根據 emits 選項推導暴露在 setup 上下文中的 emit 函數的類型:
import { defineComponent } from 'vue'
export default defineComponent({
emits: ['change'],
setup(props, { emit }) {
emit('change') // <-- 類型檢查 / 自動補全
}
})
默認推導類型
ref 會根據初始化時的值自動推導其類型:
import { ref } from 'vue'
// 推導出的類型:Ref<number>
const year = ref(2020)
// => TS Error: Type 'string' is not assignable to type 'number'.
year.value = '2020'
通過接口指定類型
有時我們可能想為 ref 內的值指定一個更復雜的類型,可以使用 Ref 這個接口:
import { ref } from 'vue'
import type { Ref } from 'vue'
const year: Ref<string | number> = ref('2020')
year.value = 2020 // 成功!
通過泛型指定類型
或者,在調用 ref() 時傳入一個泛型參數,來覆蓋默認的推導行為:
// 得到的類型:Ref<string | number>
const year = ref<string | number>('2020')
year.value = 2020 // 成功!
如果你指定了一個泛型參數但沒有給出初始值,那么最后得到的就將是一個包含 undefined 的聯合類型:
// 推導得到的類型:Ref<number | undefined>
const n = ref<number>()
默認推導類型
reactive() 也會隱式地從它的參數中推導類型:
import { reactive } from 'vue'
// 推導得到的類型:{ title: string }
const book = reactive({ title: 'Vue 3 指引' })
通過接口指定類型
要顯式地指定一個 reactive 變量的類型,我們可以使用接口:
import { reactive } from 'vue'
interface Book {
title: string
year?: number
}
const book: Book = reactive({ title: 'Vue 3 指引' })
默認推導類型
computed() 會自動從其計算函數的返回值上推導出類型:
import { ref, computed } from 'vue'
const count = ref(0)
// 推導得到的類型:ComputedRef<number>
const double = computed(() => count.value * 2)
// => TS Error: Property 'split' does not exist on type 'number'
const result = double.value.split('')
通過泛型指定類型
你還可以通過泛型參數顯式指定類型:
const double = computed<number>(() => {
// 若返回值不是 number 類型則會報錯
})
在處理原生 DOM 事件時,應該給事件處理函數的參數正確地標注類型。讓我們看一下這個例子:
<script setup lang="ts">
function handleChange(event) {
// `event` 隱式地標注為 `any` 類型
console.log(event.target.value)
}
</script>
<template>
<input type="text" @change="handleChange" />
</template>
沒有類型標注時,這個 event 參數會隱式地標注為 any 類型。這也會在 tsconfig.json 中配置了 "strict": true 或 "noImplicitAny": true 時報出一個 TS 錯誤。因此,建議顯式地為事件處理函數的參數標注類型。此外,你可能需要顯式地強制轉換 event 上的屬性:
function handleChange(event: Event) {
console.log((event.target as HTMLInputElement).value)
}
provide 和 inject 通常會在不同的組件中運行。要正確地為注入的值標記類型,Vue 提供了一個 InjectionKey 接口,它是一個繼承自 Symbol 的泛型類型,可以用來在提供者和消費者之間同步注入值的類型:
import { provide, inject } from 'vue'
import type { InjectionKey } from 'vue'
const key = Symbol() as InjectionKey<string>
provide(key, 'foo') // 若提供的是非字符串值會導致錯誤
const foo = inject(key) // foo 的類型:string | undefined
建議將注入 key 的類型放在一個單獨的文件中,這樣它就可以被多個組件導入。
當使用字符串注入 key 時,注入值的類型是 unknown,需要通過泛型參數顯式聲明:
const foo = inject<string>('key') // 類型:string | undefined
注意注入的值仍然可以是 undefined,因為無法保證提供者一定會在運行時 provide 這個值。當提供了一個默認值后,這個 undefined 類型就可以被移除:
const foo = inject<string>('foo', 'bar') // 類型:string
如果你確定該值將始終被提供,則還可以強制轉換該值:
const foo = inject('foo') as string
模板 ref 需要通過一個顯式指定的泛型參數和一個初始值 null 來創建:
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const el = ref<HTMLInputElement | null>(null)
onMounted(() => {
el.value?.focus()
})
</script>
<template>
<input ref="el" />
</template>
注意為了嚴格的類型安全,有必要在訪問 el.value 時使用可選鏈或類型守衛。這是因為直到組件被掛載前,這個 ref 的值都是初始的 null,并且 v-if 將引用的元素卸載時也會被設置為 null。
有時,我們需要為一個子組件添加一個模板 ref,以便調用它公開的方法。比如,我們有一個 MyModal 子組件,它有一個打開模態框的方法:
<!-- MyModal.vue -->
<script setup lang="ts">
import { ref } from 'vue'
const isContentShown = ref(false)
const open = () => (isContentShown.value = true)
defineExpose({
open
})
</script>
為了獲取 MyModal 的類型,我們首先需要通過 typeof 得到其類型,再使用 TypeScript 內置的 InstanceType 工具類型來獲取其實例類型:
<!-- App.vue -->
<script setup lang="ts">
import MyModal from './MyModal.vue'
const modal = ref<InstanceType<typeof MyModal> | null>(null)
const openModal = () => {
modal.value?.open()
}
</script>
Ok,以上就是在 Vue3 組件中使用 TS 類型的基本方法,也是我最近的 Vue3 學習筆記。歡迎在評論區交流討論,一起學習成長!
如果對你有所幫助,不要忘了點贊支持一下哦 ~
*請認真填寫需求信息,我們會在24小時內與您取得聯系。