TypeScipt
中為了使編寫的代碼更加規(guī)范盅安,更有利于維護(hù),增加了類型校驗(yàn)世囊,所以以后寫ts
代碼必須要指定類型
在
TypeScript
后面可以不指定類型别瞭,但是后期不能改變其類型,不然會報(bào)錯(cuò)株憾,但是只會報(bào)錯(cuò)蝙寨,不會阻止代碼編譯,因?yàn)?code>JS是可以允許的
1. 基本類型
1.1 布爾類型(boolean
)
let flag: boolean = true;
flag = 123; // 報(bào)錯(cuò),因?yàn)椴环项愋?flag = false; // 正確寫法,boolean類型只能寫 true 和 false
1.2 數(shù)字類型(number
)
let num: number = 123;
console.log(num);
1.3 字符串類型(string
)
let str: string = "string";
console.log(str);
1.4 數(shù)組類型(Array
)
在TypeScript
中有兩種定義數(shù)組的方法
// 第一種定義數(shù)組方法
let arr1: number[] = [1.2.3]; // 一個(gè)全是數(shù)字的數(shù)組
// 第二種定義數(shù)組方法
let arr2: Array<number> = [1,2,3];
1.5 只讀數(shù)組類型(ReadonlyArray
)
只讀數(shù)組中的數(shù)組成員和數(shù)組本身的長度等屬性都不能夠修改嗤瞎,并且也不能賦值給原賦值的數(shù)組
let a: number[] = [1,2,3,4];
let ro: ReadonlyArray<number> = a; // 其實(shí)只讀數(shù)組只是一個(gè)內(nèi)置定義的泛型接口
ro[1] = 5; // 報(bào)錯(cuò)
ro.length = 5; // 報(bào)錯(cuò)
a = ro; // 報(bào)錯(cuò),因?yàn)閞o的類型為Readonly,已經(jīng)改變了類型
a = ro as number[]; // 正確,不能通過上面的方法復(fù)制,但是可以通過類型斷言的方式
1.6 元組類型(tuple
)
元組類型屬于數(shù)組的一種墙歪,上面一種數(shù)組里面只能有一種類型,否則會報(bào)錯(cuò)贝奇,而元組類型內(nèi)部可以有多種類型
// 元組類型可以為數(shù)組中的每個(gè)成員指定一個(gè)對應(yīng)的類型
let arr: [number,string] = [123,"string"]; // 注意如果類型不對應(yīng)也會報(bào)錯(cuò)
arr[2] = 456; // 報(bào)錯(cuò)
// 因?yàn)樵M類型在聲明時(shí)是一一對應(yīng)的,這里只能有2個(gè)成員
1.7 枚舉類型(enum
)
/*
通過:
enum 枚舉名{
標(biāo)識符 = 整形常數(shù),
標(biāo)識符 = 整形常數(shù),
......
標(biāo)識符 = 整形常數(shù)
};
定義一個(gè)枚舉類型
*/
enum Color {
Red = 1,
Blue = 3,
Oragne = 5
}
let color: Color = Color.Red; // color為Color枚舉類型,值為Color中的Red,也就是1
console.log(color); // 1
注意:
- 如果沒有給標(biāo)識符賦值虹菲,那么標(biāo)識符的值默認(rèn)為索引值
- 如果在期間給某個(gè)標(biāo)識符進(jìn)行了賦值,而之后的標(biāo)識符沒有賦值掉瞳,那么之后表示符的索引依次為前面的值加
1
- 可以把標(biāo)識符用引號括起來毕源,效果不受影響
- 還可以通過反過來選擇索引值來獲取字符串標(biāo)識符
enum Color {
Red,
Blue = 3,
"Oragne"
}
let color1: Color = Color.Red;
let color2: Color = Color.Blue;
let color3: Color = Color.Oragne;
let color4: string = Color[0]; // 通過獲取索引得到標(biāo)識符
console.log(color1); // 0
console.log(color2); // 3
console.log(color3); // 4
console.log(color4); // red
1.8 任意類型(any
)
任意類型的數(shù)據(jù)就像JS
一樣,可以隨意改變其類型
let num:any = 123;
num = "string";
console.log(num); // string
具體用法
// 如果在ts中想要獲取 DOM 節(jié)點(diǎn),就需要使用任意類型
let oDiv: any = document.getElementsByTagName("div")[0];
oDiv.style.backgroundColor = "red";
// 按道理說DOM節(jié)點(diǎn)應(yīng)該是個(gè)對象,但是TypeScript中沒有對象的基本類型,所以必須使用 any 類型才不會報(bào)錯(cuò)
1.9 undefined
和null
類型
雖然為變量指定了類型陕习,但是如果不賦值的話默認(rèn)該變量還是undefined
類型脑豹,如果沒有指定undefined
直接使用該變量的話會報(bào)錯(cuò),只有自己指定的是undefined
類型才不會報(bào)錯(cuò)
let flag1: undefined; // 也可以 let flag1: undefined = undefined;
console.log(flag); // undefined
let flag2: null = null; // 如果不指定值為null那么打印的時(shí)候會報(bào)錯(cuò)
console.log(flag2); // null
為變量指定多種可能的類型
let flag: number | undefined; // 這種寫法就不會在沒有賦值的時(shí)候報(bào)錯(cuò)了,因?yàn)樵O(shè)置了可能為undefined
console.log(flag); // undefined
flag = 123;
console.log(flag); // 123 也可以改為數(shù)值類型
// 也可以設(shè)定多個(gè)類型
let flag1: number | string | null | undefined;
flag1 = 123;
console.log(flag1); // 123
flag1 = null;
console.log(flag1); // null
flag1 = "string";
console.log(flag1); // string
1.10 void
類型
TypeScript
中的void
表示沒有任何類型衡查,一般用于定義方法的時(shí)候沒有返回值瘩欺,雖然也能給變量指定類型,但是void
類型只能被賦值undefined
和null
拌牲,沒有什么實(shí)際意義
在
TypeScript
中函數(shù)的類型表示其返回值的類型俱饿,函數(shù)類型為void
表示其沒有返回值
function run(): void {
console.log(123);
// return 不能有返回值,否則報(bào)錯(cuò)
}
function run1(): number {
console.log(456);
return 123; // 必須有返回值,并且返回值為number類型,否則報(bào)錯(cuò)
}
function run2(): any {
console.log(789); // 因?yàn)閍ny是任意類型,所以也可以不要返回值
}
1.11 never
類型
never
類型是其他類型(包括null
和undefine
)的子類型,代表著從來不會出現(xiàn)的值塌忽,意味著聲明never
類型的變量只能被never
類型所賦值
let a: undefined;
a = undefined;
let b: null;
b = null;
let c: never; // c不能被任何值賦值,包括 null 和 undefiend
c = (() => {
throw new Error("error!!");
})(); // 可以這樣賦值
// 返回never的函數(shù)必須存在無法達(dá)到的終點(diǎn)
function error(message: string): never {
throw new Error(message);
}
// 返回never的函數(shù)必須存在無法達(dá)到的終點(diǎn)
function infiniteLoop(): never {
while (true) {
}
}
// 推斷的返回值類型為never
function fail() {
return error("Something failed");
}
1.12 object
類型
object
表示非原始類型拍埠,也就是除number
,string
土居,boolean
枣购,symbol
嬉探,null
和undefined
之外的類型
let stu: object = {name:"張三", age:18};
console.log(stu); // {name: "張三", age: 18}
// 也可以
let stu: {} = { name: "張三", age: 18 };
console.log(stu); // {name: "張三", age: 18}
declare function create (o: object | null): void;
// declare是一個(gè)聲明的關(guān)鍵字,可以寫在聲明一個(gè)變量時(shí)解決命名沖突的問題
create({ prop: 0 }); // OK
create(null); // OK
create(42); // 報(bào)錯(cuò)
create("string"); // 報(bào)錯(cuò)
create(false); // 報(bào)錯(cuò)
create(undefined); // 報(bào)錯(cuò)
2. 高級類型
2.1 交叉類型
交叉類型是將多個(gè)類型合并為一個(gè)類型。這讓我們可以把現(xiàn)有的多種類型疊加到一起成為一種類型棉圈,它包含了所需的所有類型的特性
function extend<T, U>(first: T, second: U): T & U {
let result = <any>{}; // 官方這里是T&U,但是會報(bào)錯(cuò),因?yàn)椴恢猅和U是不是對象,必須any才可以
for (let id in first) {
(<any>result)[id] = (<any>first)[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = (<any>second)[id];
}
}
return result;
}
class Person {
constructor(public name: string) { }
}
interface Loggable {
log(): void;
}
class ConsoleLogger implements Loggable {
log() {
// ...
}
}
var jim = extend(new Person("Jim"), new ConsoleLogger());
var n = jim.name;
jim.log();
2.2 聯(lián)合類型
聯(lián)合類型與交叉類型很有關(guān)聯(lián)涩堤,但是使用上卻完全不同,我們使用的用豎線隔開每一個(gè)類型參數(shù)的時(shí)候使用的就是聯(lián)合類型
聯(lián)合類型表示一個(gè)值可以是幾種類型之一分瘾。 用豎線( |
)分隔每個(gè)類型胎围,所以 number | string | boolean
表示一個(gè)值可以是 number
,string
德召,或 boolean
function padLeft(value: string, padding: string | number) {
// ...
}
let indentedString = padLeft("Hello world", true); // errors,只能是string 或 number
如果一個(gè)值是聯(lián)合類型白魂,我們只能訪問此聯(lián)合類型的所有類型里共有的成員
interface Bird {
fly();
layEggs();
}
interface Fish {
swim();
layEggs();
}
function getSmallPet(): Fish | Bird { // 這的報(bào)錯(cuò)先不管
// ...
}
let pet = getSmallPet(); // 因?yàn)闆]有明確返回哪個(gè)類型,所以只能確定時(shí)Fish和Bird類型中的一個(gè)
pet.layEggs(); // 我們可以調(diào)用共有的方法
pet.swim(); // errors,不能調(diào)用一個(gè)類型的方法,因?yàn)槿f一是Bird類型就不行了
2.3 類型保護(hù)
聯(lián)合類型適合于那些值可以為不同類型的情況。 但當(dāng)我們想確切地了解是否為 Fish
時(shí)上岗,JavaScript
里常用來區(qū)分2
個(gè)可能值的方法是檢查成員是否存在福荸,但是在TypeScript
中必須要先使用類型斷言
let pet = getSmallPet();
if ((<Fish>pet).swim) {
(<Fish>pet).swim();
} else {
(<Bird>pet).fly();
}
2.3.1 用戶自定義類型保護(hù)
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined; // 返回一個(gè)布爾值,然后TypeScript會縮減該變量類型
}
/*
pet is Fish就是類型謂詞。謂詞為 parameterName is Type 這種形式肴掷,parameterName必須是來自于當(dāng)前函數(shù)簽名里的一個(gè)參數(shù)名
*/
// 'swim' 和 'fly' 調(diào)用都沒有問題了,因?yàn)榭s減了pet的類型
if (isFish(pet)) {
pet.swim();
} else {
pet.fly();
}
TypeScript
不僅知道在if
分支里pet
是Fish
類型敬锐,它還清楚在else
分支里,一定 不是Fish
類型捆等,一定是Bird
類型
2.3.2 typeof
類型保護(hù)
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
這些
typeof
類型保護(hù)只有兩種形式能被識別:typeof v === "typename"
和typeof v !== "typename"
,"typename"
必須是"number"
续室,"string"
栋烤,"boolean"
或"symbol"
。 但是TypeScript
并不會阻止你與其它字符串比較,語言不會把那些表達(dá)式識別為類型保護(hù)
2.3.3 instanceof
類型保護(hù)
interface Padder {
getPaddingString(): string
}
class SpaceRepeatingPadder implements Padder {
constructor(private numSpaces: number) { }
getPaddingString() {
return Array(this.numSpaces + 1).join(" ");
}
}
class StringPadder implements Padder {
constructor(private value: string) { }
getPaddingString() {
return this.value;
}
}
function getRandomPadder() {
return Math.random() < 0.5
? new SpaceRepeatingPadder(4)
: new StringPadder(" ");
}
// 類型為SpaceRepeatingPadder | StringPadder
let padder: Padder = getRandomPadder();
if (padder instanceof SpaceRepeatingPadder) {
padder; // 類型細(xì)化為'SpaceRepeatingPadder'
}
if (padder instanceof StringPadder) {
padder; // 類型細(xì)化為'StringPadder'
}
instanceof
的右側(cè)要求是一個(gè)構(gòu)造函數(shù)挺狰,TypeScript
將細(xì)化為:
- 此構(gòu)造函數(shù)的
prototype
屬性的類型明郭,如果它的類型不為any
的話- 構(gòu)造簽名所返回的類型的聯(lián)合
2.3.4 可以為null
的類型
如果沒有在vscode
中,直接編譯的話是可以給一個(gè)其他類型的值賦值為undefined
或者null
的丰泊,但是如果編譯時(shí)使用了--strictNullChecks
標(biāo)記的話薯定,就會和vscode
一樣不能賦值了,并且可選參數(shù)和可以選屬性會自動(dòng)加上| undefined
類型
2.3.5 類型保護(hù)與類型斷言
由于可以為null
的類型是通過聯(lián)合類型實(shí)現(xiàn)瞳购,那么需要使用類型保護(hù)來去除 null
function f(sn: string | null): string {
if (sn === null) {
return "default";
} else {
return sn;
}
}
// 也可以使用下面的形式
function f(sn: string | null): string {
return sn || "default";
}
如果編譯器不能夠去除 null
或 undefined
话侄,你可以使用類型斷言手動(dòng)去除。 語法是添加 !
后綴:identifier!
從 identifier
的類型里去除了 null
和 undefined
function broken(name: string | null): string {
function postfix(epithet: string) {
return name.charAt(0) + '. the ' + epithet; // error, 'name' is possibly null
}
name = name || "Bob";
return postfix("great");
}
// 上面的函數(shù)會報(bào)錯(cuò)
function fixed(name: string | null): string {
function postfix(epithet: string) {
// 這里的name強(qiáng)制斷言不是null或者undefined,因?yàn)樵谇短缀瘮?shù)中不知道name是否為null
return name!.charAt(0) + '. the ' + epithet; // ok
}
name = name || "Bob"; // 這里已經(jīng)明確表明返回的name肯定是字符串
return postfix("great"); // 但是由于是嵌套函數(shù),這里還不知道
}
/*
嵌套函數(shù)中: 因?yàn)榫幾g器無法去除嵌套函數(shù)的null(除非是立即調(diào)用的函數(shù)表達(dá)式)学赛。因?yàn)樗鼰o法跟蹤所有對嵌套函數(shù)的調(diào)用,尤其是將內(nèi)層函數(shù)做為外層函數(shù)的返回值年堆。如果無法知道函數(shù)在哪里被調(diào)用,就無法知道調(diào)用時(shí)name的類型
*/
2.4 類型別名
類型別名會給一個(gè)類型起個(gè)新名字。類型別名有時(shí)和接口很像,但是可以作用于原始值盏浇,聯(lián)合類型变丧,元組以及其它任何需要手寫的類型
別名不會創(chuàng)建一個(gè)新的類型,而是創(chuàng)建了一個(gè)新的名字來引用那個(gè)類型
type Name = string; // 通過type關(guān)鍵詞創(chuàng)建一個(gè)別名
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
同接口一樣绢掰,類型別名也可以是泛型痒蓬,可以添加類型參數(shù)并且在別名聲明的右側(cè)傳入
type Container<T> = { value: T };
// 我們也可以使用類型別名來在屬性里引用自己
type Tree<T> = {
value: T;
left: Tree<T>;
right: Tree<T>;
}
// 與交叉類型一起使用童擎,我們可以創(chuàng)建出一些十分稀奇古怪的類型
type LinkedList<T> = T & { next: LinkedList<T> };
interface Person {
name: string;
}
var people: LinkedList<Person>;
var s = people.name; // 注意這種寫法在vscode中會報(bào)錯(cuò),因?yàn)楦訃?yán)格,必須先賦值再使用
var s = people.next.name;
var s = people.next.next.name;
var s = people.next.next.next.name;
類型別名不能出現(xiàn)在聲明右側(cè)的任何地方
type Yikes = Array<Yikes>; // 報(bào)錯(cuò)
2.5 字符串自變量類型
字符串字面量類型允許指定字符串必須的固定值。在實(shí)際應(yīng)用中攻晒,字符串字面量類型可以與聯(lián)合類型顾复、類型保護(hù)和類型別名很好的配合。通過結(jié)合使用這些特性炎辨,可以實(shí)現(xiàn)類似枚舉類型的字符串
type Easing = "ease-in" | "ease-out" | "ease-in-out";
class UIElement {
animate(dx: number, dy: number, easing: Easing) {
if (easing === "ease-in") {
// ...
} else if (easing === "ease-out") {
} else if (easing === "ease-in-out") {
} else {
// error! should not pass null or undefined.
}
}
}
let button = new UIElement();
button.animate(0, 0, "ease-in");
button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here
// 只能從三種允許的字符中選擇其一來做為參數(shù)傳遞,傳入其它值則會產(chǎn)生錯(cuò)誤
字符串字面量類型還可以用于區(qū)分函數(shù)重載
function createElement(tagName: "img"): HTMLImageElement;
function createElement(tagName: "input"): HTMLInputElement;
// ... more overloads ...
function createElement(tagName: string): Element {
// ... code goes here ...
}