Please enable Javascript to view the contents

TypeScript快问快答

 ·  ☕ 6 分钟

1. 如何理解TS中的any、unknow和never?

any 支持几乎任何类型,不包含never、unknown、void。
unknow 表示尚不清楚的类型,但可以表示所有类型的联合。当一个值是从外部获取的不能提前知道类型,等使用的时候我们再去做断言类型操作。
never 不包含任何元素,空集。当在处理异常和错误情况时,never可以表示该变量为不应该出现的类型。

2. type和interface有什么区别?

type 类型别名
interface 声明接口(ts的interface描述一个对象的属性,java c#中的interface为描述功能)
interface用面向对的方式解释type,关键词extends 相当于type中联合类型

区别1:
type 可以描述所有数据:基本类型和对象
interface 只能描述对象

区别2:
type 只是类型的别名(ts会推导出已存在的类型)
interface 则是声明类型(interface会创建一个新的类型)

区别3:
type不可以重新赋值
而interface会自动合并,可以进行字段扩展(理解:描述对象的时候可以加一个属性)
所以对外API尽量使用interface,方便扩展。
对内API尽量使用type,防止代码分散。

interface Person {
	name: string
}

interface Person {
	age: number
}

区别4:
interface在extends时如果有属性冲突会直接报错(报错更明显)
而type不会报错,会将该属性的类型变为never,甚至直接把当前type变为never


3. 联合类型变量在使用时怎么类型收窄?(什么是类型收窄?)

联合类型变量在定义的时候是多个类型的并集,在使用的时候需要区分类型(类型收窄 narrowing)

  1. 用typeof(有局限,数组对象、普通对象、日期对象、null,typeof都返回object)
  2. 使用instanceof区分类型(有局限,不支持string、number、boolean、TS独有类型)
  3. 结合1和2,普通类型使用typeof,对象类型使用instanceof(局限:TS类型无法区分)
  4. 在3的基础上,使用keyA in TypeA,关键词in来区分TS类型
  5. 以上4点都是使用js的方法来区分类型,可以使用TS的类型谓词is来判断任意类型(支持所有类型,但比较麻烦需要声明一个函数)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
type Rect = {
	width: number;
	height: number;
}

const a = { width: 10, height: 20 }
if (isRect(a)) {
	return a;
} else {
	return null;
}

// 这里使用is后,isRect使用的时候a的类型会判断为Rect,反之如果只写boolean则a的类型不会自动判断
function isRect(x: Rect | Circle): x is Rect { 
	return 'height' in x && 'width' in x;
}
  1. 自己在对象类型中添加一个可识别字段,比如kind: ‘xx’,我定义圆形类型的kind一定为circle,方形类型的kind一定为rect,这样就可以通过kind来区分类型了。一般使用kind、type,要求所有要区分的类型都有kind字段;是kind需要时基本类型;各类型间的kind可区分。

4. 当交叉类型有字段类型冲突的时候,该字段会是什么类型?

答案是never。
例如:

type A = {
	age: number;
}

type B = {
	age: string;
}

type C = A & B;

const c: C = {
	age: '123' // age的类型是never
}

5. 如何断言绑定事件的回调函数?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface MyEvent {
	target: HTMLElement;
}

interface MyMouseEvent extends MyEvent {
	x: number;
	y: number;
}

function listenEvent(eventType: string, callback: (e: MyEvent) => void) {
	//...
}

// 断言回调函数中使用的参数
listenEvent("click", (e: MyEvent) =>
	console.log((e as MyMouseEvent).x + "," + (e as MyMouseEvent).y);
);

// 断言整个回调函数
listenEvent("click", ((e: MyMouseEvent) =>
	console.log(e.x + "," + e.y)) as (e: MyEvent) => void 
);

或者 在tsconifg中把straictFunctionTypes设置为false

或者用 as unknown as xxx


6. as const 怎么使用?

as const 可以用来收紧类型。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
let a = '1'; // a的类型为string
const b = '1'; // b的类型为'1',因为const无法赋值,类型收紧了

let c = [1, 2]; // c的类型为number[]
const d = [1, 2] as const; // d的类型为readonly [1, 2]

let e = { x: 1, y: 2 }; // e的类型为{ x: number, y: number }
const f = { x: 1, y: 2 } as const; // f的类型为{ readonly x: 1, readonly y: 2 }

// as const 经常会应用在对象和数组上,用于类型收紧
function sum(name: number, ...array: number[]) {
	// 虽然数组a是const,但是依然可以push,使用as const后可收紧类型为readonly [1, 2],长度为2
	const a = [1, 2] as const;
	f(...a); // f的参数只有两个,as const前a的长度不确定会报错
}

function f(a: number, b: number) {
	return a + b;
}

7. ts有哪些语法可以从类型中创造新类型?

extends 包含于
keyof 获取一个对象类型的所有key
T[‘name’] 获取类型T中下标为name的类型,其中name extends keyof T
in keyof 主要在map类型中使用
-readonly[] 使得对应类型从只读变为可操作
-? 使得可选类型变为必填类型
as 断言操作,对一个类型做进一步的判断


8. interface和class的区别?

interface只有成员类型没有实现
普通class必须有成员的类型和实现
abstract class(抽象类)的成员类型可以实现也可以不实现

所以class一般用于实现interface,而且一个class可以同时实现多个interface

ps: 在class中也可以使用索引签名

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Hash {
	[s: string]: unknown

	set(key: string, value: unknown) {
		this[key] = value;
	}

	get(key: string) {
		return this[key]
	}
}

9. 什么是infer?

infer可以显式地声明一个泛型变量。这样描述很抽象来看例子:

1
2
3
4
// 这里infer是对Item的引用
// 可以不用考虑Array里Item的具体类型,就可以直接返回改类型
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;

10. 类型声明文件和类型作用域有什么关系?

默认情况下*.d.ts中的type和interface全局生效,
但是如果*.d.ts文件中有import或者export则type和interface只在当前模块生效,
此时可以用declare global包裹type和interface使其全局生效;

当为js文件写类型声明文件时,如果是全局作用域的类型则可以直接对应js文件中的变量和函数,如果是局部作用域的类型则可以用declare module包裹或者用同名文件(如下);

main.js
main.d.js
同名文件可以有一一对应的关系,可以视作在一个作用域中.

11 全局变量中的document和Element类型是谁声明的?

我们可以在tsconfig中的compilerOptions.lib中添加DOM来支持DOM相关方法的类型.
相同的全局变量中的Object,Array,Set,Map…是由ESNext来支持相关类型的.

默认会加载@types包下所有的类型文件,如果需要指定加载的类型文件可以在tsconfig中的compilerOptions.types中指定.

分享

Llane00
作者
Llane00
Web Developer