定义的时候有赋值,将会推断成当前值的类型。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; }