-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Exploring TypeScript Type Annotations - Defining Types
作者: zhilidali
欢迎来到 《探索 TypeScript 类型注解》 系列教程。
上一篇介绍了 TypeScript 的原生数据类型,本篇探索如何创建自定义类型 (约定类型首字母大写),来描述程序中复杂的数据类型。
目录
1 接口
接口通过 interface
关键字定义,定义的类型可以用来描述对象、数组、函数、类等。
// 定义声明一个 MyType 类型
interface MyType {}
对象结构
基本语法: K: T
描述对象的形状(结构)
// 定义属性 a 为字符串类型,属性 b 为数值类型
interface MyType {
a: string;
b: number;
}
// 使用自定义的 MyType 类型
let bar: MyType = { a: 'TS', b: 1 }; // Ok
let baz: MyType = { a: 1000, b: 1 }; // Error
let foo: MyType = { a: 'TS' }; // Error (结构不一致)
可选属性: K?: T
interface MyType {
a: string;
b?: number
}
let foo: MyType = { a: 'TS' }; // Ok
只读属性: readonly K: T
interface MyType {
a: string;
readonly b: number;
}
let bar: MyType = { a: 'TS', b: 1 };
bar.a = 'TypeScript'; // Ok
bar.b = 2; // Error
索引签名
- 字符串索引签名 String Index Signature
- 数值索引签名 Number Index Signature
字符串索引签名:[K: string]: T
描述任意属性
interface MyType {
a: string;
[k: string]: string;
}
let baz: MyType = { a: 'TS', b: 'foo' };
数值索引签名:[K: number]: T
描述数组类型
interface MyType {
[index: number]: string
}
let arr1: MyType = ['a', 'b', 'c']; // Ok
let arr2: MyType = ['a', 'b', 100]; // Error
调用签名
调用签名 Call Signature: (parameter: T): U
描述函数 (参数和返回值) 类型
interface FnType {
(a: number): boolean; // 参数 a 为 number 类型,返回 boolean 类型
}
let fn: FnType = (n: number) => !n;
接口与类
类具有实例与静态两部分构成。
- implements 实现接口:
class Foo implements T {}
描述类的实例部分 - Construct Signature 构造器签名 =>
new (): T
描述类的构造函数
// 可通过 implements 描述类的实例部分
interface ClassType {
foo: string;
setFoo(str: string): void;
}
// 构造器签名用来描述构造函数
interface ConstructorType {
new (n: number): any;
}
const Foo: ConstructorType = class Foo implements ClassType {
foo: string = 'foo';
setFoo(str: string) {
this.foo = str;
}
constructor(n: number) { }
}
类作为接口
在 TS 中,类不仅会声明类类变量,还声明了一个类型,所以类可以作为接口使用
class A {
x: number = 1;
foo(): number { return 1 }
}
const a: A = new A(); // `: A` 作为接口使用
a.x;
a.foo();
接口继承 Interface Extending
接口也可以如同 JS 的类一样,可以使用 extends
实现继承。
- Interface Extending Interfaces 接口继承接口
- Interface Extending Classes 接口继承类 (类可以作为接口)
interface Type1 { foo: string }
// 接口 Type2 继承接口 Type1
interface Type2 extends Type1 { bar: string }
const obj1: Type2 = {
foo: 'foo',
bar: 'bar',
};
class Baz {
baz: string;
constructor(str: string) {
this.baz = str;
}
}
// 接口 Type3 继承类 Baz
interface Type3 extends Baz { qux: string }
const obj2: Type3 = {
baz: 'baz',
qux: 'qux',
};
混合类型
混合类型 Hybrid Type: 比如 JS 中的函数也可以作为对象,为其添加属性
interface MyType {
(n: number): string;
a: number;
}
// 函数 foo 本质是对象,可添加属性 a
function foo(n: number): string { return '' }
foo.a = 123;
let bar: MyType = foo;
2 类型别名
Type Alias 类型别名使用 type
关键字,创建一个引用其他类型的新名字。
type Str = string;
let s: Str = 'foo';
type Obj = {
a: number;
}
let obj: Obj = { a: 1 };
类型别名 VS. 接口
type
创建的名字引用其它类型。
- 类型别名可以命名原始类型、元组等
interface
创建了一个新的类型。
- 接口可以继承
- 接口可以声明合并 (后续篇幅会介绍)
3 泛型
Generic 泛型好比 JS 中的函数,使用尖括号包裹类型参数:
<T>
。
泛型变量
泛型变量 Generic Type Variable: <T, U...>
- Generic Function 泛型函数
- Generic Interface 泛型接口
- Generic Class 泛型类
泛型函数 : <T>(): ReturnType
// 通过 <T> 传递类型参数,下面示例约束参数和返回值类型一致
function identity<T>(arg: T): T {
return arg;
}
// 泛型好比 JS 中的函数,使用时需传入类型
let foo = identity<string>('TS');
// 由于TS 会自动推断类型,可省略类型传参
let bar = identity('TS');
泛型接口 :interface TypeName<T> {}
// 使用 interface 描述上面函数
interface GenericInterface1 {
<T>(arg: T): T;
}
// 可将泛型参数当作整个接口的参数
interface GenericInterface2<T> {
(arg: T): T;
}
let baz: GenericInterface2<string> = identity;
泛型类 :class className<T> {}
// 泛型类与泛型接口差不多,`<>`跟在类名后面
class GenericClass<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;
};
泛型约束
假设一个函数接受一个参数,返回参数的属性 foo。
function getFoo<T> (arg: T) {
return arg.foo; // Error: Property 'foo' does not exist on type 'T'
}
使用泛型变量定义,由于事先不知道参数的类型,会显示错误,我们可以使用泛型约束 <T extends U>
。
interface FooProp {
foo: number;
}
// 泛型 `T` 必须符合接口 `FooProp`,即必须包含 `foo` 属性
function getFoo<T extends FooProp> (arg: T) {
return arg.foo;
}
getFoo({ foo: 3 }); // Ok, getFoo<{ foo: number}>({ foo: 3})
getFoo({ foo: 'foo' }); // Error
结语
本篇介绍了如何自定义类型,下篇介绍类型的检查规则。
协议
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。
《探索 TypeScript 类型注解》
- To pick up a draggable item, press the space bar. While dragging, use the arrow keys to move the item. Press space again to drop the item in its new position, or press escape to cancel.
Activity