TypeScript中的类型

TypeScript中的类型

什么是TypeScript

TypeScriptJavaScript的超集,通过添加静态类型定义与检查来对JavaScript进行扩展。以下简称TS

Playground

点击这里进入TypeScript的Playground

常见的类型

原始数据类型

JavaScript中的原始类型在在TS中都有对应的类型:

  • string
  • number
  • boolean
  • undefined
  • null
  • symbol
  • bigint
  • object: 上面几个都是原始类型,object表示所有的非原始哦类型,包括数组、对象、函数等,在实际应用中尽量不适用该类型。
1
2
3
4
5
6
7
8
const str: string = 'str'
const num: number = 0
const bool: boolean = false
const un: undefined = undefined
const n: null = null
const sym: symbol = Symbol('sym')
const bint: bigint = BigInt(9999999999999999)
const obj: object = {}

其他类型

数组

1
2
const arr1:string[] = [] // 每一项都是字符串的数组
const arr2:Array<number> = [] // 每一项都是数字的数组

元组

1
2
3
const tup1:[string, number] = ['shy', 18] // 元组 第一项为字符串 第二项为数字 共两项
const tup2:[string, number?] = ['shy'] // 元组 第一项为字符串 第二项为数字 共两项 第二项是可选的可以没有
const tup3:[name: string, age?: number] // 可以给每一项命名

函数

1
2
3
4
5
6
function func1(x: number, y: number): number {
return x * y
}
// func1 的类型为 (x: number, y: number) => number
const func2: (x: number, y: number) => number = (x, y) => x + y
const func3 = (x: number, y: number): number => x + y

void

JavaScript中,void 允许在表达式执行完成时,产生(某些地方)期望获得的 undefined 值。
void 运算符通常只用于获取 undefined 的原始值,一般使用 void(0)(等同于 void 0)。在上述情况中,也可以使用全局变量 undefined 来代替。
详见MDN

TSvoid描述一个函数没有显示返回值是的类型。

any

any类型表示任意类型,赋予该类型将不会进行类型推断和类型校验 开发中尽量少使用该类型。

unknown

unknown类型标识一个未知的类型。

any 与 unknown

  • 任意类型都能赋值给 anyany 也可以赋值给任意类型;任意类型都能赋值给 unknown,但是 unknown 只能赋值给 unknown/any 类型;
  • unknown 在不进行类型推断的时候,无法直接使用;any 则没有这个限制;
  • 建议:
    • 类型不兼容时使用 any:推荐使用 as 进行类型断言
    • 类型太复杂不想写使用 any:推荐使用 as 进行类型断言,找到你所需要的最小单元
    • 不清楚具体类型是什么而使用 any:推荐声明时使用 unknown 来代替,在具体调用的地方再进行断言

never

never类型标识不存在的类型。

字面量类型

指一些具体的值作为类型,一般与联合类型一起使用:

1
2
const num: 1 | 2 = 1 // 当前num只是赋值1或者2
const str: 'shy' | 'hang' = 'shy'

枚举

枚举是TS扩展的类型,使用enum关键字来声明。

1
2
3
4
5
enum PaperType {
A4 = 'A4',
A3 = 'A3',
A5 = 'A5',
}

上述代码编译为JavaScript

1
2
3
4
5
6
7
"use strict";
var PaperType;
(function (PaperType) {
PaperType["A4"] = "A4";
PaperType["A3"] = "A3";
PaperType["A5"] = "A5";
})(PaperType || (PaperType = {}));

接口

接口 interface 是对行为的抽象, TypeScript 里常用来对对象进行描述

1
2
3
4
5
interface Person {
name: string // name为字符串
readonly age: number // age为数值 readonly: 只读属性
address?: string // address为字符串 可选的
}

interface 与 type

TS官方文档

  • type 可以用来定义原始类型、联合/交叉类型、元组等,interface 则不行
  • interface 声明的同名类型可以进行合并,而 type 则不可以,会报标识符重复的错误
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    interface Person1 {
    name: string
    }
    interface Person1 {
    age: number
    }
    let person: Person1 // person 的类型为{name:string; age: number}
    type Person2 = {
    name: string
    }
    // 'Person2' is already defined.
    type Person2 = {
    age: number
    }
  • interface 会有索引签名的问题,而 type 没有
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    interface Demo1 {
    name: string
    }
    type Demo2 = {
    name: string
    }
    const data1: Demo1 = { name: 'name1' }
    const data2: Demo2 = { name: 'name2' }
    interface PropType {
    [key: string]: string
    }
    let prop: PropType
    prop = data1 // 不能将类型“Test1”分配给类型“PropType”。类型“Test1”中缺少类型“string”的索引签名。ts(2322)
    prop = data2 // 正常

建议:官方推荐使用 interface,当 interface 无法满足,例如需要定义联合类型等,再选择使用 type

联合类型

表示一组可用的类型集合,只要属于其中之一就属于这个联合类型

1
const union: string | number = 'union' // 表示union可以使string类型也可以是number类型

交叉类型

表示一组类型的叠加,需要满足所有条件才可以属于这个交叉类型,一般用于接口的合并

1
2
3
4
5
6
7
8
9
10
11
12
interface A {
name: string
}
interface B {
age: number
}
const x: A & B = { name: 'shy', age: 18 }
// 如果两个类型出现重复的key则每一个key都会进行交叉
// never
type AA = number
type BB = string
type U = A & B // U 的类型就会是nuver

泛型

泛型是一种抽象类型,只有在调用时才知道具体的类型。如果将类型类比为函数,那么泛型就相当于函数中的参数了。

简单理解:类型层面的参数

1
2
3
function add<T>(a: T): T {
return a
}

typeof

typeof 可以获取变量或者属性对应的类型,返回的是一个 TS 类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let str = 'shy'
type STR = typeof str // string
// ----------------------------------------------------------------
let obj = {
name: 'shy',
age: 18,
address: 'Qingdao',
c: 1 as any
}

type OBJ = typeof obj
// type OBJ = {
// name: string;
// age: number;
// address: string;
// c: any;
// }

keyof

keyof 用于获取类型中所有的键,返回一个联合类型:

1
2
3
4
5
6
7
type OBJ = {
name: string;
age: number;
address: string;
c: any;
}
type OBJKEY = keyof OBJ // "name" | "age" | "address" | "c"

in

in 用于遍历类型:

1
2
3
4
5
6
7
8
9
10
11
12
type OBJKEY = "name" | "age" | "address" | "c"

type OBJ = {
[key in OBJKEY]: string
}
// 相当于
// type OBJ = {
// name: string;
// age: string;
// address: string;
// c: string;
// }

extends

继承

1
2
3
4
5
6
7
8
type A<T extends string> = T[]
type A1 = A<string>
type A2 = A<number> // 类型“number”不满足约束“string”。

// extends还可以用于条件判断
type B<T> = T extends string ? string : string[]
type B1 = B<string> // type B1 = string
type B2 = B<number> // type B2 = string[]

infer

工具类型

Partial 将所有属性变为可选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Make all properties in T optional
*/
type Partial<T> = {
[P in keyof T]?: T[P];
};
// ----------
type OBJ = {
name: string;
age: number;
address: string;
c: any;
}
type C = Partial<OBJ>
// type C = {
// name?: string | undefined;
// age?: number | undefined;
// address?: string | undefined;
// c?: any;
// }

Partial 只能将最外层的属性变为可选,类似浅拷贝,如果要想把深层地将所有属都变成可选,可以手动实现一下:

1
2
3
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P] | undefined
}

Required 将所有属性变为必选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Make all properties in T required
*/
type Required<T> = {
[P in keyof T]-?: T[P];
};
// ----------
type C = {
name?: string | undefined;
age?: number | undefined;
address?: string | undefined;
c?: any;
}

type D = Required<C>
// type D = {
// name: string;
// age: number;
// address: string;
// c: any;
// }

Readonly 将所有属性都变成只读,不可修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Make all properties in T readonly
*/
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// ----------
type OBJ = {
name: string;
age: number;
address: string;
c: any;
}
type E = Readonly<OBJ>
// type E = {
// readonly name: string;
// readonly age: number;
// readonly address: string;
// readonly c: any;
// }

Record 以指定的类型生成对应类型的键值对

以指定的类型生成对应类型的键值对,例如我们经常会使用 Record<string, unknown> 或者 Record<string, any> 来对对象的类型进行声明

1
2
3
4
5
6
7
8
9
10
11
/**
* Construct a type with a set of properties K of type T
*/
type Record<K extends keyof any, T> = {
[P in K]: T;
};
// --------
type AA = Record<string, string>
// type AA = {
// [x: string]: string;
// }

Exclude 移除属于指定类型的部分

1
2
3
4
5
6
7
/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;
// ----------
type A = Exclude<"name" | "age", "name">
// type A = "age"

Extract 保留指定类型的部分

1
2
3
4
5
6
7
8
9
/**
* Extract from T those types that are assignable to U
*/
type Extract<T, U> = T extends U ? T : never;
// ----------
type A = Extract<"name" | "age", "name">
// type A = "name"
type B = Extract<string | number | boolean, number>
// type B = number

NonNullable 去除类型中的 null 和 undefined

1
2
3
4
5
6
7
/**
* Exclude null and undefined from T
*/
type NonNullable<T> = T & {};
// ---------
type A = NonNullable<"a" | number | null | undefined>
// type A = number | "a"

Pick 以选中的属性生成新的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* From T, pick a set of properties whose keys are in the union K
*/
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
// ---------
type OBJ = {
name: string;
age: number;
address?: string;
c?: any;
}
type A = Pick<OBJ, "name" | "address">
// type A = {
// name: string;
// address?: string | undefined;
// }

Omit 排除选中的属性,以剩余的属性生成新的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Construct a type with the properties of T except for those in type K.
*/
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
// ----------
type OBJ = {
name: string;
age: number;
address?: string;
c?: any;
}
type A = Omit<OBJ, "name" | "address">
// type A = {
// age: number;
// c?: any;
// }

Parameters 获得函数参数的类型

1
2
3
4
5
6
7
8
/**
* Obtain the parameters of a function type in a tuple
*/
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
// ---------------
type FUNC = (x: number, y: number) => number
type FUNCParam = Parameters<FUNC>
// type FUNCParam = [x: number, y: number]

ReturnType 获取函数返回值的类型

1
2
3
4
5
6
7
8
/**
* Obtain the return type of a function type
*/
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
// ---------------
type FUNC = (x: number, y: number) => number
type FUNCReturnType = ReturnType<FUNC>
// type FUNCReturnType = number

tsconfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{
"compilerOptions": {
"baseUrl": ".", // baseUrl 用于设置基础 url,可以帮我们省掉一些多余的路径前缀。
"module": "ESNext", // 编译后的 JS 使用哪种模块系统。
"target": "es2016", // 指定编译的目标版本。
"lib": [ // TypeScript 默认自带通用的 JS 内置 API 的类型声明
"DOM",
"ESNext"
],
"strict": true, // 启用严格模式,能够更能保证类型检测的正确。
"jsx": "preserve", // 如何处理 .tsx 文件中对于 jsx 的生成,常用值包括:react/preserve
"esModuleInterop": true, // 开启后会生成辅助函数以兼容处理在 esm 中导入 cjs 的情况
"declaration": false, // 是否给每个编译出来的 JS 生成对应的 d.ts 类型声明文件
"declarationDir": "./types", // 指定编译生成的类型声明文件输出的目录。
"skipLibCheck": true, // 是否跳过非源代码中所有类型声明文件(.d.ts)的检查
"outDir": "./dist", // 编译文件的输出目录,默认为 .,即项目根目录。如果不设置它,编译后的文件就会和源文件混杂在一起。通常我们会将 outDir 设置为 "./dist"。
"moduleResolution": "node", // 模块解析策略,支持 node/classic,后者基本不推荐使用
"resolveJsonModule": true, // 是否支持导入 json 文件,并做类型推导和检查
"noUnusedLocals": true, // 存在未使用的参数时是否报错
"strictNullChecks": true, // 是否启用严格的 null 检查
"noUnusedParameters": false, // 存在未使用的参数时是否报错
"noEmit": false, // 是否将构建产物写入文件系统,一个常见的实践是只用 tsc 进行类型检查,使用单独的打包工具进行打包
"allowJs": true, // 将 js 文件也作为编译对象,可以被 ts 文件引入。布尔值,默认为 false。
"checkJs": false, // 是否检查 .js 文件中的错误
"forceConsistentCasingInFileNames": true,
"sourceMap": false, // 是否生成对应的 .map 文件
"paths": { // 路径重映射。
"@/": [
"./src"
]
},
"types": [] // 类型声明的一种引入方式是 @types 包,比如 React 框架使用了 flow 作为类型系统,为了支持 TypeScript,React 团队又写一套 d.ts 类型文件,发布到 @types/react 包上。
},
"exclude": [
"dist",
"node_modules"
],
"include": [
"src"
]
}

TypeScript中的类型
https://www.shaohang.xin/2023/02/05/technical/typescript/types/
作者
少航
发布于
2023年2月5日
许可协议