typescript面试

6,375 阅读6分钟

【持续更新】

1. 什么是typescript

TS是一个强类型的JavaScript超集,支持所有JS已有语法,支持面向对象编程的概念,如:类、接口、继承、泛型等。TS并不直接在浏览器上运行,需要编译成JS来运行

2. 为什么要使用ts,ts的优势是什么?

增加了静态类型,可以在编译脚本的时候检测错误,使得代码质量更好,更健壮。 优势:

  1. 杜绝手误导致变量名写错
  2. 类型可以一定程度上充当文档
  3. IDE自动填充,支持联想;

3. ts中const和readonly的区别?

const可以防止变量的值被修改,
readonly可以防止变量的属性被修改。

4. 枚举和常量枚举的区别

常量枚举只能使用常量枚举表达式并且不同于常规的枚举,他们在编译阶段会被删除。常量枚举成员在使用的地方会被内联起来,之所以真可以这么做是因为,常量枚举不允许包含计算成员。

5. interface和type区别

相同点:

  1. 都可以描述对象或者函数
  2. 都允许扩展(extends)

不同点:

  1. 类型别名可以为任何类型引入名称,例如基本类型,联合类型等。
  2. 类型别名不支持继承
  3. 类型别名不会创建一个类型的名字
  4. 类型别名无法被实现implements,而接口可以被派生类实现
  5. 类型别名重名会抛出错误,接口重名是会产生合并

6. TS中any的作用是什么?

为编程阶段还不清楚的变量一个类型,这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。这种情况下,我们不希望类型检查器对这些值进行检查而是直接让他们通过编译阶段的检查

7. any、never、unknown、null、undefined、void有什么区别

any: 动态的变量类型(丢失了类型检查的作用)
never: 永不存在的值的类型,例如:never类型是那些总是抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型。
unknown:任何类型的值都可以赋值给unknown类型,但是unknown只能赋值给unknown本身和any类型。若需要复制给其他类型前,必须被断言。
null和undefined:默认情况是所有类型的子类型。可以把这两个类型赋值给number类型。但是当我们指定strictNullChecks: true标记是,他们只能赋值给自身类型。
void:没有任何类型。例如:一个函数没有返回值,那么返回值可以定义为void。

8. interface可以给Function/Array/Class做声明吗?

可以

interface Say {
    (name: string): void;
}
let say: Say = (name: string): void => {};

interface NumberArray {
    [index: number]: number;
}
let fibonacci: NumberArray = [1, 2, 3, 4];

interface Person {
    name: string;
    sayHi(name: string): string;
}

9. ts中的this和js中的this有什么区别?

noImplicitThis: true的情况下,必须去声明this的类型,才能在函数或者对象中使用this。
ts中箭头函数的this和ES6中箭头函数中的this是一致的。

10. 使用Union Types时有那些注意事项

属性或方法访问:当ts不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里面共有的属性或方法。

function getLength(str: string | number): number {
    return str.length;
}
// index.ts(2,22): error TS2339: Property 'length' does not exist on type >'string | number'.
// Property 'length' does not exist on type 'number'.

function getString(str: string | number): string {
    return str.toString();
}
// 公共方法和属性可以访问

11. 如何设计Class的声明

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet(): string {
        return 'hello ' + this.greeting;
    }
}

12. 获取枚举类型的key

enum Str {
    A, B, C
}
type strUnion = keyof typeof str; // 'A' | 'B' | 'C'

13. ts中?.、??、!、!.、_、** 符号的含义

?.可选链遇到null和undefined可以立即停止表达式的运行。
??空值合并运算符当左侧操作数为null或undefined时,其返回右侧的操作数,否则返回左侧的操作数。
!非空断言运算符x!将从x值域中排出null和undefined。
!.在变量名后添加,可以断言排除undefined和null
_数字分隔符分隔符不会改变数字字面量的值,是人更容易读懂数字。例如 1_101_123。
**求幂

14. 简单介绍下模块的加载机制

假设有一个导入语句import {a} from 'moduleA'

  1. 首先,编译器会尝试定位需要导入的模块文件,通过绝对或者相对的路径查找方式。
  2. 如果上面的解析失败了,没有查找到对应的模块,编译器会尝试定位一个外部模块声明(.d.ts)
  3. 最后,如果编译器还是不能解析这个模块,则会排出一个错误error TS2307: Cannot find module 'moduleA'.

15. declare,declare global是什么?

declare是用来定义全局变量、全局函数、全局命名空间、js modules、class等
declare global 为全局对象window增加的新属性

declare global {
    interface Window {
        csrf: string;
    }
}

16. keyof和typeof关键字的作用

keyof 索引类型查询操作符 获取索引类型的属性名,构成联合类型。
typeof获取一个变量或对象的类型

17. Exclude、Omit、Merge、Intersection、Overwrite的作用

Exclude<T, U>从Y中排出可以分配给U的元素.
Omit<T, K>忽略T中的某些属性.
Merge<O1, O2>将两个对象的属性合并.
Compute<A & B>将交叉类型合并.
Intersection<T, U>取T的属性,此属性同样哦存在于U.
Overwrite<T, U>用U的属性覆盖T的相同属性.

18. 什么是抗变、双变、协变和逆变

Covariant协变,ts对象兼容性是协变,父类<=子类是可以的,子类 <= 父类错误。
Contravariant逆变,禁用strictFunctionTypes编译,函数参数类型都是逆变的,父类 <= 子类,是错误。子类 <= 父类,是可以的。
Bivariant 双向协变,函数参数的类型默认是双向协变的。父类 <= 子类,是可以的。子类 <= 父类,是可以的。

19. implements与extends的区别

extends 子类会继承父类的所有属性和方法
implements 使用该关键字的类将需要实现的需要实现的类的所有属性和方法。

20. 枚举和object的区别

  1. 枚举可以通过枚举的名称,获取枚举的值,也可以通过枚举值获取枚举的名称
  2. object只能通过key获取value
  3. 数字枚举在不置顶初始值的情况下,枚举值会从0开始递增
  4. 虽然在运行时,枚举是一个真实存在的对象,但是使用keyof时的行为却和普通对象不一致,必须使用keyof typeof才可以获取枚举的所有属性名。

21. never 和void的区别

never表示永远不存在的类型,比如一个函数总是排出错误,而没有返回值。或者一个函数内部有死循环,永远不会有返回值。韩式的返回值就是never类型。
void没有显式的返回值的函数的返回值类型为void,如果一个变量为void类型,只能赋予undefined或null。

22. 如何在window扩展类型

delcalre global {
    interface Window {
        myCustomFn: () => void;
    }
}

23.实现类型

内置高级用法

Record<K, V>

type Key = 'a' | 'b' | 'c';
const a: Record<Key, string> = {
    a: 'a',
    b: 'b',
    c: 'c'
}

type Record<K extends number | string | symbol, V> {
    [Key in K]: V
}

Eclude<T, K>

type Foo = 'a' | 'b' | 'c';
type A = Exclude<Foo, 'a'>; // 'b' | 'c'

type Exclude<T, K> = T extens K ? never : T;

Extract<T, U>

type Key = 'a' | 'b' | 'c';
type A = Extract<Key, 'a'>; // 'a'

type Extract<T, U> = T extends U ? T : never;

Omit<T, K>

type Keys = {
    a: string;
    b: number;
    c: boolean;
}
type A = Keys<Keys, 'a' | 'b'>; // {c: boolean}

type Omit<T, K extends number | string | symbol> = {
    [Key in Exclude<keyof T, K>]: T[Key]
}

NonNullable<T>

type Foo = 'a' | 'b' | null | undefined;
type A = NonNullable<Foo>; 'a' | 'b'

type NonNullable<T> = T extends null | undefined ? never : T;

Partial<T>;

将属性全部变为可选属性

type Foo = {
    a: string;
    b: number;
    c: boolean;
}
const a: Partial<Foo> = {};
const b: Partial<Foo> = { b: 12 };

type Patial<T> = {
    [K in keyof T]?: T[K];
}

Required<T>

把属性全部变为必须属性

type Foo = {
    a?: string;
    b?: number;
    c: boolean;
}
const a: Required<Foo> = {a: 'qwe'} // Error

const b: Required<Foo> = {a: '23', b: 1, c: false}; // Ok

type Required<T> = {
    [K in keyof T]-?: T[K];
}

24. ts文件可以直接在浏览器中运行吗?

没有使用ts语法的 ts文件,可以直接在新版本的谷歌浏览器中运行了 使用ts语法,如变量变量类型声明等语法,必须编译成js文件后,使用js文件运行