TypeScript 相关问题
1.枚举和常量枚举(const枚举)的区别
枚举会被编译时会编译成一个对象,可以被当作对象使用
const枚举会在ts编译期间被删除,避免额外的性能开销
// 代码示例 // 编译之前 enum test { a=1, b=1 } const enum testconst { aa=1, bb=1 } // 编译之后 var test; (function (test) { test[test["a"] = 1] = "a"; test[test["b"] = 1] = "b"; })(test || (test = {})); //可以看到编译之后只有enum枚举并没有const枚举
2.ts中如何获取枚举联合类型的key?
type Name = { name: string }
type Age = { age: number }
type Union = Name | Age
// infer可以理解为条件句型中的类型推导
type UnionKey<P> = P extends infer P ? keyof P : never
type T = UnionKey<Union>
3.const和readonly的区别
const用于变量,readonly用于属性
const在运行时检查,readonly在编译时检查
使用const变量保存的数组,可以使用push,pop等方法。但是如果使用
ReadonlyArray<number>
声明的数组不能使用push,pop等方法。4.ts中 ?.、??、!.等符号的含义?
?. 可选链
?? ?? 类似与短路或,??避免了一些意外情况0,NaN以及””,false被视为false值。只有undefind,null被视为false值。
!. 在变量名后添加!,可以断言排除undefined和null类型
_ , 声明该函数将被传递一个参数,但您并不关心它
// ?? let x = foo ?? bar(); // 等价于 let x = foo !== null && foo !== undefined ? foo : bar(); // !. let a: string | null | undefined a.length // error a!.length // ok 复制代码
5.枚举和 object 的区别
枚举可以通过枚举的名称,获取枚举的值。也可以通过枚举的值获取枚举的名称。
object只能通过key获取value
数字枚举在不指定初始值的情况下,枚举值会从0开始递增。
虽然在运行时,枚举是一个真实存在的对象。但是使用keyof时的行为却和普通对象不一致。必须使用keyof typeof才可以获取枚举所有属性名。
6.never、void 的区别
never,never表示永远不存在的类型。比如一个函数总是抛出错误,而没有返回值。或者一个函数内部有死循环,永远不会有返回值。函数的返回值就是never类型。
void, 没有显示的返回值的函数返回值为void类型。如果一个变量为void类型,只能赋予undefined或者null。
7.unkonwn和any
unknown 类型是 TS3 新增的类型,这个类型与 any 类型类似,可以设置任何的类型值,随后可以更改类型。因此,我们可以将变量先设置为字符串类型,然后再将其设置为数字类型,如果事先不检查类型,使用any类型,调用了不存在的方法,编译时不会报错,代码运行时才会发现错误。但是使用unknown 类型不一样,如果不进行类型判断,执行相关操作编译器就会报错。
any
可以看到执行tsc any.ts时并不会报错,但是执行node any.ts会报错
- unknown
它认为unknown类型,这个类型没有push方法,当然会报错,除非先判断类型,如果是相关类型且正确执行相关方法,编译器则会顺利通过,虽然有些麻烦,但是相比 any 类型说,更加安全,在代码编译期间,就能帮我们发现由于类型造成的问题,因此在大多的场景,建议使用 unknown 类型替代 any。
let val:unknown = 18;
val = "wxy";
val = new Array();
if (val instanceof Array) {
val.push(33);
}
console.log(val);
8.interfaces 与 type 之间有什么区别
与接口类型不一样,类型别名可以用于一些其他类型,比如原始类型、联合类型和元组:
// primitive type name = string; // object type posX = { x: number; }; type posY = { y: number; }; // union type pos = posX | posY; // tuple type Data = [number, string];
- 接口和类型别名都能够被扩展,但语法有所不同。此外,接口和类型别名不是互斥的。接口可以扩展类型别名,而反过来是不行的。
- 类型别名无法被实现(implements),而接口可以被派生类实现
- 类型别名重名时编译器会抛出错误,接口重名时会产生合并
9.Object、object 和 {}
- object类型:它用于表示非原始类型。
- JavaScript 中以下类型被视为原始类型:
string
、boolean
、number
、bigint
、symbol
、null
和undefined
。
const proto = {};
Object.create(proto); // OK
Object.create(null); // OK
Object.create(undefined); // Error
Object.create(1337); // Error
Object.create(true); // Error
Object.create("oops"); // Error
Object类型:它是所有 Object 类的实例的类型。它由以下两个接口来定义、
Object
包括原始interface Object { constructor: Function; toString(): string; toLocaleString(): string; valueOf(): Object; hasOwnProperty(v: PropertyKey): boolean; isPrototypeOf(v: Object): boolean; propertyIsEnumerable(v: PropertyKey): boolean; } interface ObjectConstructor { new(value?: any): Object; (value?: any): any; readonly prototype: Object; getPrototypeOf(o: any): any; // ... } declare var Object: ObjectConstructor;
{} 类型:它描述了一个没有成员的对象。当你试图访问这样一个对象的任意属性时,TypeScript 会产生一个编译时错误。