Skip to content

探索 TypeScript 类型注解 - 自定义类型 #9

@zhilidali

Description

@zhilidali
Member

Exploring TypeScript Type Annotations - Defining Types

本文由 WowBar 团队首发于 GitHub

作者: zhilidali

欢迎来到 《探索 TypeScript 类型注解》 系列教程。
上一篇介绍了 TypeScript 的原生数据类型,本篇探索如何创建自定义类型 (约定类型首字母大写),来描述程序中复杂的数据类型。

目录

  1. 接口
  2. 类型别名
  3. 泛型

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

结语

本篇介绍了如何自定义类型,下篇介绍类型的检查规则。

协议

CC BY-NC-ND 4.0

本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

《探索 TypeScript 类型注解》

参考链接

Handbook: https://github.com/microsoft/TypeScript-Handbook

Metadata

Metadata

Assignees

Labels

ExploringTSExploring TypeScriptTypeScriptJavaScript that scales

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @zhilidali

      Issue actions

        探索 TypeScript 类型注解 - 自定义类型 · Issue #9 · WowBar/blog