函数
函数声明时,需要给出参数的类型和返回值的类型。参数类型不指定时,会推断为 any。返回值类型 TypeScript 会自己推断,但是推荐写明确。
- 实际使用时,一般用 type 为函数类型定义一个别名。
- 函数的实际参数个数可以少于类型指定的参数个数,但不能多。
- x?: number 代表可选参数,需要放在末尾。
- x: numer | undefined = 0 类型的参数如果在前面,需要传 undefined 才能使用默认值
// 声明函数的写法
const hello = function (txt: string) {
console.log('hello ' + txt);
}
const hello: (txt: string) => void = function (txt) {
console.log('hello ' + txt);
};
// 这种方法适用于函数本身存在属性时
let hello: {
(txt: string): void;
};
函数重载
有些函数可以接受不同类型或不同个数的参数,并且根据参数的不同,会有不同的函数行为,这种行为称为函数重载(function overload)。
- 所有类型的参数必须在一个函数内处理。
- 不同重载的参数不能冲突。
- TypeScript 从上向下检查类型,所以范围越宽的,需要放在后面。
function reverse(str:string):string;
function reverse(arr:any[]):any[];
function reverse(stringOrArray: string|any[]): string|any[] {
if (typeof stringOrArray === 'string') {
return stringOrArray.split('').reverse().join('');
} else {
return stringOrArray.slice().reverse();
}
}
构造函数
- 构造函数的类型的写法,是在参数列表前加 new 关键字。
- JavaScript 中,class 本质上是一种构造函数。
class Animal {
numLegs: number = 4;
}
type AnimalConstructor = new () => Animal;
function create(c: AnimalConstructor): Animal {
return new c();
}
const a = create(Animal);
// 对象形式定义
type F = {
new (s:string): object;
};
// 既可以是构造函数,又可以是普通函数
type F = {
new (s:string): object;
(n?:number): number;
}
对象
- 可选属性 x?: number。
- 只读属性 readonly。只读属性只能在初始化时赋值,之后不能修改。如果只读属性是个对象,对象的属性可以修改,但是不能整个替换对象。
// 写法一
type MyObj = {
x: number;
y: number;
};
const obj: MyObj = { x: 1, y: 1 };
// 写法二
interface MyObj {
x: number;
y: number;
}
const obj: MyObj = { x: 1, y: 1 };
interface
interface 是对象的模板,可以看做是一种类型的约定。使用了某个模板的对象,就拥有了指定的类型结构。
// 声明 interface
interface Person {
firstName: string;
lastName: string;
age: number;
}
// 使用 interface
const p: Person = {
firstName: 'John',
lastName: 'Smith',
age: 25,
};
继承
- 继承 interface:interface 可以多重继承,但要求继承或覆盖时,属性兼容。
- interface 可以继承 type
- interface 可以继承 class
interface Style {
color: string;
}
interface Shape {
name: string;
}
interface Circle extends Style, Shape {
radius: number;
}
接口合并
TypeScript 会对同名的 interface 进行自动合并:
- 同名属性有多个类型声明时,要求类型兼容
- 同名方法合并时,会发生函数重载,一般情况下越后面的优先级更高。但是,如果函数的参数是字面量,会排在函数重载的最前面。
interface Document {
createElement(tagName: any): Element;
}
interface Document {
createElement(tagName: "div"): HTMLDivElement;
createElement(tagName: "span"): HTMLSpanElement;
}
interface Document {
createElement(tagName: string): HTMLElement;
createElement(tagName: "canvas"): HTMLCanvasElement;
}
// 等同于
interface Document {
createElement(tagName: "canvas"): HTMLCanvasElement;
createElement(tagName: "div"): HTMLDivElement;
createElement(tagName: "span"): HTMLSpanElement;
createElement(tagName: string): HTMLElement;
createElement(tagName: any): Element;
}
interface 与 type 的异同
- type 可以表示非对象类型,interface 只能表示对象类型(包括数组、函数等)。
- interface 可以继承,type 不支持继承。type 通过 & 符号,实现在其他 type 包括 interface 的基础上添加属性,类似于继承;interface 使用的是继承。
// interface 继承 type
type Foo = { x: number; };
interface Bar extends Foo {
y: number;
}
// type
interface Foo {
x: number;
}
type Bar = Foo & { y: number; };
- 同名 interface 会自动合并,同名 type 会报错。
- interface 不支持属性映射。
- this 关键字只能用于 interface。
- type 可以扩展原始数据类型,interface 不行。
- interface 无法表达某些复杂类型(比如交叉类型和联合类型),type 可以。
type A = { /* ... */ };
type B = { /* ... */ };
// 联合类型
type AorB = A | B;
// 为联合类型添加属性
type AorBwithName = AorB & {
name: string
};
综上所述,如果有复杂的类型运算,只能选择 type。在一般情况下,interface 的灵活性更高,建议优先使用。