定義的時候有賦值,將會推斷成當前值的類型。web
let myFavoriteNumber = 'seven'; myFavoriteNumber = 7; // index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.
定義的時候沒有賦值,無論以後有沒有賦值,都會被推斷成any類型而徹底不被類型檢查。數組
let myFavoriteNumber; myFavoriteNumber = 'seven'; myFavoriteNumber = 7;
聯合類型的變量在被賦值的時候,會根據類型推論的規則推斷出一個類型:函數
let myFavoriteNumber: string | number; myFavoriteNumber = 'seven'; console.log(myFavoriteNumber.length); // 5 myFavoriteNumber = 7; console.log(myFavoriteNumber.length); // 編譯時報錯 // index.ts(5,30): error TS2339: Property 'length' does not exist on type 'number'.
上例中,第二行的 myFavoriteNumber 被推斷成了 string,訪問它的 length 屬性不會報錯。
而第四行的myFavoriteNumber被推斷成了number,訪問它的 length 屬性時就報錯了。ui
interface Person { readonly id: number; //只讀屬性 name: string; age?: number; // 可選屬性的類型必須是任意屬性類型的子集 [propName: string]: string; // 定義任意屬性 } let tom: Person = { id: 89757, // 只讀屬性須要在此時定義 name: 'Tom', age: 25, gender: 'male' }; tom.id \= 89757; // 只讀屬性不能在此時定義,會報錯。 // index.ts(3,5): error TS2411: Property 'age' of type 'number' is not assignable to string index type 'string'. // index.ts(7,5): error TS2322: Type '{ [x: string]: string | number; name: string; age: number; gender: string; }' is not assignable to type 'Person'. // Index signatures are incompatible. // Type 'string | number' is not assignable to type 'string'. // Type 'number' is not assignable to type 'string'. // index.ts(8,5): error TS2322: Type '{ name: string; gender: string; }' is not assignable to type 'Person'. // Property 'id' is missing in type '{ name: string; gender: string; }'. // index.ts(13,5): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.
let fibonacci: number[] = [1, 1, 2, 3, 5];
interface NumberArray { [index: number]: number; }
function sum() { let args: { [index: number]: number; length: number; callee: Function; } = arguments; }
let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];
function reverse(x: number | string): number | string { if (typeof x === 'number') { return Number(x.toString().split('').reverse().join('')); } else if (typeof x === 'string') { return x.split('').reverse().join(''); } }
然而這樣有一個缺點,就是不可以精確的表達,輸入爲數字的時候,輸出也應該爲數字,輸入爲字符串的時候,輸出也應該爲字符串。this
function reverse(x: number): number; function reverse(x: string): string; function reverse(x: number | string): number | string { if (typeof x === 'number') { return Number(x.toString().split('').reverse().join('')); } else if (typeof x === 'string') { return x.split('').reverse().join(''); } }
TypeScript 會優先從最前面的函數定義開始匹配,因此多個函數定義若是有包含關係,須要優先把精確的定義寫在前面。3d
語法:值 as 類型
或<類型>值
代碼中使用any類型,咱們也能夠選擇改進它,經過類型斷言及時的把 any斷言爲精確的類型,亡羊補牢,使咱們的代碼向着高可維護性的目標發展。code
function getCacheData(key: string): any { // 函數返回any return (window as any).cache[key]; } interface Cat { name: string; run(): void; } const tom = getCacheData('tom') as Cat; // 在使用的時候將其斷言成具體類型。 tom.run();
總結:對象
A
可以被斷言爲 B
,只須要 A
兼容 B
或 B
兼容 A
便可as any as Foo
繼承
interface Cat { run(): void; } interface Fish { swim(): void; } function testCat(cat: Cat) { return (cat as any as Fish); // 雙重斷言 }
除非無可奈何,千萬別用雙重斷言。接口
類型斷言只會影響 TypeScript 編譯時的類型,類型斷言語句在編譯結果中會被刪除:
function toBoolean(something: any): boolean { return something as boolean; } toBoolean(1); // 返回值爲 1
function toBoolean(something: any): boolean { return Boolean(something); } toBoolean(1); // 返回值爲 true
爲了增長代碼的質量,咱們最好優先使用類型聲明,這也比類型斷言的 as
語法更加優雅。
interface Animal { name: string; } interface Cat { name: string; run(): void; } const animal: Animal = { name: 'tom' }; let tom = animal as Cat; let tom: Cat = animal; // 會報錯 // error TS2741: Property 'run' is missing in type 'Animal' but required in type 'Cat'
animal
斷言爲 Cat
,只須要知足 Animal
兼容 Cat
或 Cat
兼容 Animal
便可animal
賦值給 tom
,須要知足 Cat
兼容 Animal
才行function getCacheData<T>(key: string): T { return (window as any).cache[key]; } interface Cat { name: string; run(): void; } const tom = getCacheData<Cat>('tom'); tom.run();
經過給 getCacheData
函數添加了一個範型 <T>
,咱們能夠更加規範的實現對 getCacheData
返回值的約束,這也同時去除掉了代碼中的 any
,是最優的一個解決方案。
類型別名經常使用於聯合類型。
type Name = string; type NameResolver = () => string; type NameOrResolver = Name | NameResolver; function getName(n: NameOrResolver): Name { if (typeof n === 'string') { return n; } else { return n(); } }
type EventNames = 'click' | 'scroll' | 'mousemove'; function handleEvent(ele: Element, event: EventNames) { // do something } handleEvent(document.getElementById('hello'), 'scroll'); // 沒問題 handleEvent(document.getElementById('world'), 'dbclick'); // 報錯,event 不能爲 'dbclick' // index.ts(7,47): error TS2345: Argument of type '"dbclick"' is not assignable to parameter of type 'EventNames'.
定義一對值分別爲 string
和 number
的元組:
let tom: [string, number]; tom[0] = 'Tom'; tom[1] = 25; tom[0].slice(1); tom[1].toFixed(2);
可是當直接對元組類型的變量進行初始化或者賦值的時候,須要提供全部元組類型中指定的項。
let tom: [string, number]; tom = ['Tom']; // Property '1' is missing in type '[string]' but required in type '[string, number]'.
new
生成Cat
和 Dog
都繼承自 Animal
,可是分別實現了本身的 eat
方法。此時針對某一個實例,咱們無需瞭解它是 Cat
仍是 Dog
,就能夠直接調用 eat
方法,程序會自動判斷出來應該如何執行 eat
public
表示公有屬性或方法interface Alarm { alert(): void; } class Door { } // 防盜門 繼承 門 繼承 接口告警 class SecurityDoor extends Door implements Alarm { alert() { console.log('SecurityDoor alert'); } } // 車 繼承 接口告警 class Car implements Alarm { alert() { console.log('Car alert'); } }
一個類能夠實現多個接口:
interface Alarm { alert(): void; } interface Light { lightOn(): void; lightOff(): void; } class Car implements Alarm, Light { alert() { console.log('Car alert'); } lightOn() { console.log('Car light on'); } lightOff() { console.log('Car light off'); } }
interface Alarm { alert(): void; } interface LightableAlarm extends Alarm { lightOn(): void; lightOff(): void; }
常見的面嚮對象語言中,接口是不能繼承類的,可是在 TypeScript
中倒是能夠的:
class Point { x: number; y: number; constructor(x: number, y: number) { this.x = x; this.y = y; } } interface Point3d extends Point { z: number; } let point3d: Point3d = {x: 1, y: 2, z: 3};
爲何 TypeScript 會支持接口繼承類呢?
實際上,當咱們在聲明 class Point
時,除了會建立一個名爲 Point
的類以外,同時也建立了一個名爲 Point
的類型(實例的類型),因此咱們既能夠將 Point
當作一個類來用(使用 new Point
建立它的實例),也能夠將 Point
當作一個類型來用(使用 : Point
表示參數的類型):
class Point { x: number; y: number; constructor(x: number, y: number) { this.x = x; this.y = y; } } function printPoint(p: Point) { // 看成參數的類型 console.log(p.x, p.y); } printPoint(new Point(1, 2)); // 看成類,建立實例
值得注意的是,PointInstanceType
相比於 Point
,缺乏了 constructor
方法,這是由於聲明 Point
類時建立的 Point
類型是不包含構造函數的。另外,除了構造函數是不包含的,靜態屬性或靜態方法也是不包含的(實例的類型固然不該該包括構造函數、靜態屬性或靜態方法)。
我的理解:接口至關於類產生的類型
class Point { /** 靜態屬性,座標系原點 */ static origin = new Point(0, 0); /** 靜態方法,計算與原點距離 */ static distanceToOrigin(p: Point) { return Math.sqrt(p.x * p.x + p.y * p.y); } /** 實例屬性,x 軸的值 */ x: number; /** 實例屬性,y 軸的值 */ y: number; /** 構造函數 */ constructor(x: number, y: number) { this.x = x; this.y = y; } /** 實例方法,打印此點 */ printPoint() { console.log(this.x, this.y); } } interface PointInstanceType { x: number; y: number; printPoint(): void; } let p1: Point; let p2: PointInstanceType;
泛型(Generics)是指在定義函數、接口或類的時候,不預先指定具體的類型,而在使用的時候再指定類型的一種特性。
function swap<T, U>(tuple: [T, U]): [U, T] { return [tuple[1], tuple[0]]; } swap([7, 'seven']); // ['seven', 7]
在函數內部使用泛型變量的時候,因爲事先不知道它是哪一種類型,因此不能隨意的操做它的屬性或方法:
function loggingIdentity<T>(arg: T): T { console.log(arg.length); return arg; } // index.ts(2,19): error TS2339: Property 'length' does not exist on type 'T'.
interface Lengthwise { length: number; } function loggingIdentity<T extends Lengthwise>(arg: T): T { console.log(arg.length); return arg; }
interface CreateArrayFunc<T> { (length: number, value: T): Array<T>; } let createArray: CreateArrayFunc<any>; //此時在使用泛型接口的時候,須要定義泛型的類型。 createArray = function<T>(length: number, value: T): Array<T> { let result: T[] = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } createArray(3, 'x'); // ['x', 'x', 'x']
class GenericNumber<T> { zeroValue: T; add: (x: T, y: T) => T; } let myGenericNumber = new GenericNumber<number>(); myGenericNumber.zeroValue = 0; myGenericNumber.add = function(x, y) { return x + y; };
在 TypeScript 2.3 之後,咱們能夠爲泛型中的類型參數指定默認類型。當使用泛型時沒有在代碼中直接指定類型參數,從實際值參數中也沒法推測出時,這個默認類型就會起做用。
function createArray<T = string>(length: number, value: T): Array<T> { let result: T[] = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; }