在 TypeScript 中,咱们使用接口(Interfaces)来定义对象的类型。程序员
在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动须要由类(classes)去实现(implements)。web
TypeScript 中的接口是一个很是灵活的概念,除了可用于对类的一部分行为进行抽象之外,也经常使用于对「对象的形状(Shape)」进行描述。数组
interface Person { name: string; age: number; } let man: Person = { name: 'Xcat Liu', age: 25, };
上面的例子中,咱们定义了一个接口 Person,接着定义了一个变量 man,它的类型是 Person。这样,咱们就约束了 man的形状必须和接口 Person 一致。函数
接口通常首字母大写。ui
定义的变量比接口少了一些或者多了一些属性是不容许的:rest
interface Person { name: string; age: number; } let xcatliu: Person = { name: 'Xcat Liu', }; // index.ts(6,5): error TS2322: Type '{ name: string; }' is not assignable to type 'Person'. // Property 'age' is missing in type '{ name: string; }'.
可见,赋值的时候,变量的形状必须和接口的形状保持一致。code
有时咱们但愿不要彻底匹配一个形状,那么能够用可选属性:对象
interface Person { name: string; age?: number; } let xcatliu: Person = { name: 'Xcat Liu', };
interface Person { name: string; age?: number; } let xcatliu: Person = { name: 'Xcat Liu', age: 25, };
可选属性的含义是该属性能够不存在。接口
这时仍然不容许添加未定义的属性:ip
interface Person { name: string; age?: number; } let xcatliu: Person = { name: 'Xcat Liu', age: 25, website: 'http://xcatliu.com', }; // examples/playground/index.ts(9,3): error TS2322: Type '{ name: string; age: number; website: string; }' is not assignable to type 'Person'. // Object literal may only specify known properties, and 'website' does not exist in type 'Person'.
有时候咱们但愿一个接口容许有任意的属性,可使用以下方式:
interface Person { name: string; age?: number; [propName: string]: any; } let xcatliu: Person = { name: 'Xcat Liu', website: 'http://xcatliu.com', };
使用 [propName: string] 定义了任意属性取 string 类型的值。
须要注意的是,一旦定义了任意属性,那么肯定属性和可选属性都必须是它的子属性:
interface Person { name: string; age?: number; [propName: string]: string; } let xcatliu: Person = { name: 'Xcat Liu', age: 25, website: 'http://xcatliu.com', }; // index.ts(3,3): 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; website: 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'.
上例中,任意属性的值容许是 string,可是可选属性 age 的值倒是 number,number 不是 string 的子属性,因此报错了。
有时候咱们但愿对象中的一些字段只能在建立的时候被赋值,那么能够用 readonly 定义只读属性:
interface Person { readonly id: number; name: string; age?: number; [propName: string]: any; } let xcatliu: Person = { id: 89757, name: 'Xcat Liu', website: 'http://xcatliu.com', }; xcatliu.id = 9527; // index.ts(14,9): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.
上例中,使用 readonly 定义的属性 id 初始化后,又被赋值了,因此报错了。
只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候:
最简单的方法是使用「类型 + 方括号」来表示数组:
let fibonacci: number[] = [1, 1, 2, 3, 5];
数组的项中不容许出现其余的类型:
let fibonacci: number[] = [1, '1', 2, 3, 5]; // index.ts(1,5): error TS2322: Type '(number | string)[]' is not assignable to type 'number[]'. // Type 'number | string' is not assignable to type 'number'. // Type 'string' is not assignable to type 'number'.
也可使用数组泛型(Generic) Array<elemType> 来表示数组:
let fibonacci: Array<number> = [1, 1, 2, 3, 5];
一个比较常见的作法是,用 any 表示数组中容许出现任意类型:
let list: any[] = ['Xcat Liu', 25, { website: 'http://xcatliu.com' }];
在 JavaScript 中,有两种常见的定义函数的方式——函数声明(Function Declaration)和函数表达式(Function Expression):
// 函数声明(Function Declaration) function sum(x, y) { return x + y; } // 函数表达式(Function Expression) let mySum = function (x, y) { return x + y; };
一个函数有输入和输出,要在 TypeScript 中对其进行约束,须要把输入和输出都考虑到,其中函数声明的类型定义较简单:
function sum(x: number, y: number): number { return x + y; }
注意,输入多余的(或者少于要求的)参数,是不被容许的
若是要咱们如今写一个对函数表达式(Function Expression)的定义,可能会写成这样:
let mySum = function (x: number, y: number): number { return x + y; };
这是能够经过编译的,不过事实上,上面的代码只对等号右侧的匿名函数进行了类型定义,而等号左边的 mySum,是经过赋值操做进行类型推论而推断出来的。若是须要咱们手动给 mySum 添加类型,则应该是这样:
let mySum: (x: number, y: number) => number = function (x: number, y: number): number { return x + y; };
在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,须要用括号括起来,右边是输出类型。
咱们也可使用接口的方式来定义一个函数须要符合的形状:
interface SearchFunc { (source: string, subString: string): boolean; } let mySearch: SearchFunc; mySearch = function(source: string, subString: string) { return source.search(subString) !== -1; }
前面提到,输入多余的(或者少于要求的)参数,是不容许的。那么如何定义可选的参数呢?
与接口中的可选属性相似,咱们用 ? 表示可选的参数:
function buildName(firstName: string, lastName?: string) { if (lastName) { return firstName + ' ' + lastName; } else { return firstName; } } let xcatliu = buildName('Xcat', 'Liu'); let xcat = buildName('Xcat');
须要注意的是,可选参数必须接在必需参数后面。换句话说,可选参数后面不容许再出现必须参数了:
function buildName(firstName?: string, lastName: string) { if (firstName) { return firstName + ' ' + lastName; } else { return lastName; } } let xcatliu = buildName('Xcat', 'Liu'); let xcat = buildName(undefined, 'Xcat'); // index.ts(1,40): error TS1016: A required parameter cannot follow an optional parameter.
在 ES6 中,咱们容许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数:
function buildName(firstName: string, lastName: string = 'Liu') { return firstName + ' ' + lastName; } let xcatliu = buildName('Xcat', 'Liu'); let xcat = buildName('Xcat');
此时就不受「可选参数必须接在必需参数后面」的限制了:
function buildName(firstName: string = 'Xcat', lastName: string) { return firstName + ' ' + lastName; } let xcatliu = buildName('Xcat', 'Liu'); let xcat = buildName(undefined, 'Xcat');
ES6 中,可使用 ...rest 的方式获取函数中的剩余参数(rest 参数):
function push(array, ...items) { items.forEach(function(item) { array.push(item); }); } let a = []; push(a, 1, 2, 3);
事实上,items 是一个数组。因此咱们能够用数组的类型来定义它:
function push(array: any[], ...items: any[]) { items.forEach(function(item) { array.push(item); }); } let a = []; push(a, 1, 2, 3);
类型断言(Type Assertion)能够用来绕过编译器的类型推断,手动指定一个值的类型(即程序员对编译器断言)。
<类型>值 // 或 值 as 类型
当 TypeScript 不肯定一个联合类型的变量究竟是哪一个类型的时候,咱们只能访问此联合类型的全部类型里共有的属性或方法:
function getLength(something: string | number): number { return something.length; } // index.ts(2,20): error TS2339: Property 'length' does not exist on type 'string | number'. // Property 'length' does not exist on type 'number'.
而有时候,咱们确实须要在还不肯定类型的时候就访问其中一个类型的属性或方法,此时可使用类型断言,将 something 断言成 string
function getLength(something: string | number): number { if ((<string>something).length) { return (<string>something).length; } else { return something.toString().length; } }