Exploring TypeScript Type Annotations - Defining Types

关于

本文由 WowBar 团队首发于 GitHub

作者: zhilidali

欢迎来到 《探索 TypeScript 类型注解》 系列教程。
上一篇介绍了 TypeScript 的内置类型,本篇探索如何创建自定义类型。

目录

  1. 接口

  2. 类型别名
  3. 泛型

正文

1 接口

使用 interface 关键字。
interface MyType {}

对象结构

TS 基于结构类型 Structural Typing

基本语法 : K: T 描述对象的形状(结构)

// 定义属性 a 为字符串类型,属性 b 为数值类型
interface MyType {
    a: string;
    b: number;
}

// 使用自定义的 MyType 类型
let bar: MyType = { a: 'ts', b: 1 };
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' };

只读属性 : readonly K: T

interface MyType {
    a: string;
    readonly b: number;
}

let bar: MyType = { a: 'ts', b: 1 };
bar.a = 'TypeScript';
bar.b = 2; // Error

索引签名

索引签名 Index Signature

  • 字符串索引签名 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'];
let arr2: MyType = ['a', 'b', 100]; // Error

调用签名

调用签名 Call Signature 描述函数 (参数和返回值) 类型

(parameter: T): U
interface FnType {
    (a: number): boolean
}
let fn: FnType = (n: number) => !n;

Class

类具有实例与静态两部分构成。

  • 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) { }
}

接口继承

接口继承 Interface Extending

  • Interface Extending Interfaces 接口继承接口
  • Interface Extending Classes 接口继承类
interface Type1 { foo: string }
// 接口继承接口
interface Type2 extends Type1 { bar: string }

const obj: Type2 = {
    foo: 'foo',
    bar: 'bar',
}

混合类型

混合类型 Hybrid Type
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 };

3 泛型

Generic 泛型好比 JS 中的函数,使用尖括号包裹类型参数: <T>

泛型变量

泛型变量 Generic Type Variable: <T, U...>

  • Generic Function 泛型函数
  • Generic Interface 泛型接口
  • Generic Class 泛型类

泛型函数

// 使用泛型约束参数和返回值类型一致
function identity<T>(arg: T): T {
    return arg;
}

// 泛型好比 JS 中的函数,使用时需传入类型
let foo = identity<string>('TS');
// 由于类型推断,TS 会自动推断类型
let bar = identity('TS');

泛型接口

// 使用 interface 描述上面函数
interface GenericInterface1 {
    <T>(arg: T): T;
}
// 可将泛型参数当作整个接口的参数
interface GenericInterface2<T> {
    (arg: T): T;
}

let baz: GenericInterface2<string> = identity;

泛型类

// 泛型类与泛型接口差不多,`<>`跟在类名后面
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;
};

泛型约束

泛型约束 Generic Constraints: <T extends U>

使用泛型定义一个函数,返回参数的 foo 属性。会得到如下错误:

function getFoo<T> (arg: T) {
    return arg.foo; // Error: Property 'foo' does not exist on type 'T'
}

使用泛型约束:<T extends FooProp>

interface FooProp {
    foo: number;
}
// 泛型 `T` 必须符合接口 `Lengthwise`,即必须包含 `length` 属性
function getFoo<T extends FooProp> (arg: T) {
    return arg.foo;
}

getFoo({ foo: 3});
getFoo({ foo: 'foo' }); // Error

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

CC BY-NC-ND 4.0

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


zhilidali
35 声望3 粉丝

Node.js