§ 你觉得typescript和javascript对比,TypeScript有哪些优势?
TypeScript的好处:
1.Next-ES新特性:TypeScript是JavaScript的超集,具有可选的静态类型,并可以编译为纯JavaScript
2.静态类型系统:无需运行项目,即可对代码进行实时的静态类型检查,避免很多低级错误的发生
3.降低代码重构和维护的成本
TypeScript的优势:
1.可选性增强:基于语法解析TsDoc,ide增强
2.可维护性增强:在编译阶段暴露大部分错误
3.多人合作项目中,获得更好的稳定性和开发效率
4.包含于兼容所有的js特征,支持共存
5.支持渐进式引入与升级
§ TypeScript中的原始类型和内置对象有哪些?
原始类型:boolean/number/string/void/undefined/null/sumbol/bigint
其中,number类型除了支持十进制和十六进制,还支持二进制和八进制
内置对象:Boolean/Number/String/Date/RegExp/Error
BOM和DOM的内置对象:Window/Document/HTMLElement/DocumentFragment/Event/NodeList
§ TypeScript中如何定义布尔类型的变量
let on: boolean = true
let off: boolean = Boolean(0)
通过 new Boolean 的方式去声明布尔值,返回的是一个 Boolean 对象
let myBoolean: boolean = new Boolean(1)
§ TypeScript中在什么时候定义void以及定义void的意义是什么?
在JavaScript中,没有空值void的概念,在TypeScript中,可以使用void定义没有任何返回的函数
使用void定义一个函数,表示该函数没有返回值,如果函数定义了void,就无法通过return返回了
void类型一般用于我们不希望调用者关心函数返回值的情况下,比如通常的异步回调函数
§ type类型别名和interface的区别是什么?
interface用于描述数据结构,type用于描述数据类型
interface和type都可以描述一个对象或者一个函数
只有type可以声明基本类型别名,联合类型,元组等类型
§ any和unknown的区别是什么?
any可以完全不受类型系统的约束,我们可以很自由的使用any,any可以完成跳过TS的类型系统的检测
unknown比any安全一些
相同点是:在TypeScript中,any和unknown类型都属于顶级类型
不同点是:unknown需要先进行类型判断,才可以执行相应的类型操作,所以unknown可以被看成是更安全的any
§ never和void的区别是什么?
在TypeScript中,使用never类型来表示不应该存在的状态
如果声明一个函数为void的类型,则函数只能赋予undefine和null
§ ts中的联合类型,交叉类型,类型断言的区别是什么?
1.联合类型
联合类型是TypeScript中的一种高级类型,关键字是 |
作用是允许一个变量可以存储多种类型的值,比如:
const name: string | number = "10"
既可以用来声明字符串也可以用来声明数字
- 优点:
提供了更灵活的数据类型定义,适用于不确定性变量类型的情况
可以避免使用any类型,提高类型安全性
有效的支持方法重载和函数重载
- 缺点:
在使用联合类型的时候,我们需要进行额外的类型检查和类型保护,从而确保代码的正常运行
可能增加代码复杂性,需要根据不同类型进行逻辑处理
- 应用场景:
当一个变量可能有多种类型的时候,我们可以使用联合类型来定义
在不确定的一个变量具体类型的时候,我们可以使用联合类型进行类型保护
用于方法重载和函数重载,以支持不同类型的参数和返回值
2.交叉类型
交叉类型是TypeScript中的一种高级类型,关键字是 &
作用是多个类型合并成一个新的类型
交叉类型可以将多个类型合并成一个类型,合并后的类型拥有所有类型的属性和方法
交叉类型适用于需要同时具备多种类型特性的情况
type Person = {
name: string
age: number
sex?: boolean
}
type Employee = {
companyId: string
role: string
}
type UserInfo = Person & Employee
const userAdmin: UserInfo = {
name: "admin",
age: 24,
companyId: "110abc",
role: "Management"
}
3.类型断言
类型断言是TypeScript中的一种高级类型,关键字是 as
除了类型断言,还有双重断言,非空断言
1) 类型断言的用途:
-
将一个联合类型推断为其中一个类型
-
将一个父类断言为更加具体的子类
-
将任何一个类型断言为any
-
将any断言为一个具体的类型
const str = "123456";
const len = (str as string).length;
const box = document.getElementById("img") as HTMLImageElement
2) 双重断言:
-
断言失效后,可能会用到,但一般情况下不会使用
-
失效的情况:基础类型不能断言为接口
interface Info {
name: string;
age: number;
sex: boolean;
}
const name1 = "name" as any as Info;
3) 非空断言
非空断言关键字 !
非空断言会排除掉变量中的 null 和 undefeind
let flag: null | undefined | string;
flag!.toString();
§ 谈谈你对ts中的泛型的理解,以及常用的泛型有哪些?
什么是泛型?
泛型就是定义函数,接口或类的时候,不预先指定具体类型,而是在使用的时候指定具体类型
通过使用泛型可以让我们的代码具有更好的维护性,灵活性,复用性
泛型函数:
const Person = <T>(value: T): T => {
return value;
};
Person("xiaoming");
泛型接口:
interface Person<T, K> {
name: T;
age: K;
}
const me: Person<string, number> = {
name: "xiaoming",
age: 22,
};
数组泛型:
const arr1: string[] = ["aaa", "bbb", "ccc"];
const arr2: Array<number> = [1, 2, 3, 4, 5, 6];
泛型类:
interface Person<T> {
value: T;
getName: () => T;
}
class User<U> implements Person<U> {
value: U;
constructor(value: U) {
this.value = value;
}
getName(): U {
return this.value;
}
}
const getUserInfo1 = new User<Number>(22);
console.log(getUserInfo1.getName()); // 22
const getUserInfo2 = new User<String>("xiaoming");
console.log(getUserInfo1.getName()); // xiaoming
泛型约束:
我们可以通过用关键字 extends
来定义接口实现泛型约束
interface Person {
name: string;
age: number;
sex: boolean;
}
function student<T extends Person>(arg: T): T {
return arg;
}
student({ name: "xiaoming", age: 22, sex: true });
keyof:
泛型约束的另一个常见使用场景就是检查对象中的键是否存在,我们可以通过 keyof
实现获取某种类型的所有键,并且返回一个联合类型
interface Person {
name: string;
age: number;
sex: boolean;
}
type K1 = keyof Person; // "name" | "age" | "sex"
type K2 = keyof Person[]; // number | "length" | "push" | "concat" | ...
type K3 = keyof { [x: string]: Person }; // string | number
使用泛型和keyof约束参数(智能提示)
const Cat = {
name: "Kitty",
age: 2,
love: "flower",
};
function handleFn<T extends object, K extends keyof T>(obj: T, props: K) {
console.log(obj, props);
return obj[props];
}
handleFn(Cat, "age");
泛型工具类:
Partial:作用是将某个类型里的属性全部变成可选项 ?
interface Person {
name: string;
age: number;
sex: boolean;
}
function student<T extends Person>(arg: Partial<T>): Partial<T> {
return arg;
}
student({
name: "xiaoming",
});
Record:作用是将K中所有的属性的值转化为T类型
interface PageInfo {
title: string;
}
type Page = "home" | "about" | "other";
const x: Record<Page, PageInfo> = {
home: { title: "index" },
about: { title: "about" },
other: { title: "other" },
};
ReturnType:作用是用于获取函数T返回的类型
type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<(s: string) => void>; // void
type T2 = ReturnType<<T>() => T>; // {}
type T3 = ReturnType<<T extends U, U extends number[]>() => T>; // number[]
type T4 = ReturnType<any>; // any
type T5 = ReturnType<never>; // any
type T6 = ReturnType<string>; // Error
type T7 = ReturnType<Function>; // Error
function f(a: number, b: number) {
return a + b;
}
type A = ReturnType<typeof f>; // type A = number
Required:必填类型
interface User {
id: number;
name?: string;
}
const user: Required<User> = {
id: 1001,
name: "xiaoming", // 虽然加了可选符号,但是如果不填写,依然会报错
};
Readonly:只读不可写类型
interface User {
id: number;
name?: string;
}
const user: Readonly<User> = {
id: 1001,
};
user.id = 1002; // 如果加了Readonly,就无法修改了,否则会报错
Pick:作用是将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型
interface Todo {
title: string;
desc: string;
time: string;
}
type TodoPreview = Pick<Todo, "title" | "time">;
const todo: TodoPreview = {
title: "AAA",
time: "BBB",
};
Omit:作用是从Type中选取所有的属性值,然后移除属性名在Keys中的属性值
interface Todo {
title: string;
desc: string;
time: string;
}
type TodoPreview = Omit<Todo, "title">;
const todo: TodoPreview = {
desc: "AAA",
time: "BBB",
};
Exclude:作用是将某个类型中属于另一个类型的属性移除掉(排除类型)
type Dir = "up" | "down" | "left" | "right"
type dir = Exclude<Dir,"up" | "right"> // type dir = "down" | "left"
type Dir = "up" | "down" | "left" | "right"
type dir = Extract<Dir,"up" | "right"> // type dir = "up" | "right"
§ TypeScript中class类有哪些修饰符?
public: 在类的内部和外部都能访问,默认类型都是public
private:私有变量只能在类的内部访问,外部访问不到,子类也访问不到
protected:外部访问不到,只能在内部和子类中访问
§ TypeScript类的定义以及类包含哪几个模块?
TypeScript使用 class 关键字定义类
类可以包含以下几个模块(类的数据成员):
属性:字段的类里面声明变量,字段表示对象的有关数据
构造函数:类实例化时调用,可以为类的对象分配内存
方法:方法为对象要执行的操作
§ 如果我定义了两个interface,我有什么办法进行合并?
关键字:implements
interface A {
name: string
}
interface B {
age: number
}
class Person implements A, B {
name: "xiaoming"
age: 22
}
§ 讲讲你对ts中的函数重载的理解
使用函数重载,我们需要定义重载签名和实现签名,其中重载签名定义函数中每个参数的类型和函数的返回值类型,单不包含函数体,一个函数可以有多个重载签名。实现签名的参数类型和返回值类型,都需要使用更通用的类型,一个函数只能含有一个函数签名
// 重载签名
function fn(name: string): string;
function fn(name: string[]): string[];
// 实现签名
function fn(name: unknown): unknown {
if (typeof name === "string") {
return `Hello ${name}`;
} else if (Array.isArray(name)) {
return name.map((item) => `Hello ${name}`);
}
throw new Error("Unable to fn");
}
fn("Kitty");
fn(["xiaoming", "xiaohong"]);
类方法重载:
class Calculator {
add(a: number, b: number): number;
add(a: string, b: string): string;
add(a: string | number, b: number | number) {
if (typeof a === "string" || typeof b === "string") {
return a.toString() + b.toString();
}
return a + b;
}
}
§ 在静态类中,能访问public方法吗?
不能,在静态类中,只能访问static静态方法,不能访问public,private,protected
§ TypeScript支持静态类吗?为什么?
不支持,这个和C#和Java等面向对象的编程语言不同,在TypeScript中,我们可以将任何数据和函数创建按为简单对象,而无需创建包类型,因此,TypeScript不需要静态类,单例类只是TypeScript中的一个简单的对象
§ TypeScript面向对象编程的四个主要原则是什么?
封装,继承,抽象,多态
§ 你是如何理解ts中抽象类的?
在TypeScript中,抽象类是一个不能被直接实例化的类。它被用作其他类的基类,用于定义共享的属性和方法。抽象类可以包含抽象方法,这些方法只有定义,没有具体的实现。子类必须实现这些抽象方法
抽象类通过关键字abstract来声明,使用abstract修饰的方法也被称为抽象方法。一个类只要包含一个抽象方法,那么这个类也必须被声明为抽象类
抽象类的主要目的是定义一个通用的模板,供子类继承和实现。子类继承抽象类后,必须实现所有的抽象方法,否则子类自身也必须声明为抽象类
§ 抽象类和派生类的区别是什么?
可以通过抽象类或者接口来实现,定义抽象类的关键字是 abstract 来定义类和方法
§ 如何继承一个抽象类?
TypeScript支持继承类,即我们可以在创建类的时候继承一个已存在的类,这个已存在的类,就是父类,继承的类就是子类
继承类的关键字是 extends
子类无法继承父类的私有成员,方法以及属性和构造函数,其他的都可以继承
TypeScript一次只能继承一个类,TypeScript不支持继承多个类,但是TypeScript支持多重继承,比如A类继承B类,B类继承C类
§ 抽象类可以被实例化吗?派生类可以被实例化吗?
首先,抽象类和接口都包含了可以由派生类继承的成员,接口和抽象类都不能直接被实例化,但是可以声明他们的变量,然后利用堕胎性,把继承这两种类型的对象指定给他们的变量,最后通过这些变量来访问他们的成员,但是不能直接访问派生对象的其他成员
派生类只能继承一个基类,即只能直接继承一个抽象类,但可以使用一个继承链来来包含多个抽象类,而类可以使用任意多个接口
抽象类可以拥有抽象成员和非抽象成员,而接口成员则必须在使用接口的类上面实现
另外,接口的成员都是公共的,但抽象类的成员则可以是私有的,内部的,或者受保护的内部成员等
此外,接口不能包含字段,构造函数,析构函数,静态成员和常量
§ Ts中,如何定义一个枚举,枚举的关键字是什么?常见的枚举类型都有哪些?
枚举是定义常量的集合(以对象形式),定义枚举的关键字是 enum,并且只能用const定义,不能用var或者let定义,常见的枚举类型有,数字枚举,字符串枚举,异构枚举,接口枚举,const枚举,和反向映射枚举(通过value取key)
数值枚举:
如果给默认值,则从默认值之后数字依次往后排,如果不给默认值,则默认从0开始
enum Direction {
Up = 1,
Down,
Left,
Right
}
enum UserResponse {
Yes = 1,
No = 0,
}
function respond(recipient: string, message: UserResponse): void {
console.log(recipient, message);
}
respond("", UserResponse.Yes);
字符串枚举:
enum Direction {
Up = "up",
Down = "Down",
Left = "Left",
Right = "Right",
}
异构枚举:
enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = "YES",
}
反向映射枚举:
enum Enum {
A,
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"
§ Ts中,如何定义一个数组?
let arr1: Array<string> = ["red", "green", "blue"];
let arr2: Array<number> = [1, 2, 3, 4, 5, 6];
let arr3: Array<string | number> = ["red", 1, 2, "green", 3, 4, "blue", 5, 6];
let arr4: Array<any> = ["red", 1, true, false, 2, 3, 4, "green", "blue"];
let arr1: string[] = ["red", "green", "blue"];
let arr2: number[] = [1, 2, 3, 4, 5, 6];
let arr3: (string | number)[] = ["red", 1, 2, "green", 3, 4, "blue", 5, 6];
let arr4: any[] = ["red", 1, true, false, 2, 3, 4, "green", "blue"];
§ Ts中,如何定义一个元组?
TS中的元祖类型其实就是数组类型的扩展,元祖用于保存定长定数据类型的数据
let arr1: [string, number, boolean] = ["xiaoMing", 22, true];
§ Ts中的命名空间是什么,如何在TypeScript中声明命名空间?
定义命名空间的关键字是 namespace
在TS1.5的版本后,推荐全面使用namespace
来代替module
命名空间是用于对功能进行逻辑分组的一种方式
命名空间用于在内部维护TypeScript的遗留代码
它封装了共享某些关系的特性和对象
命名空间页称为内部模块
命名空间还可以包括接口,类,函数和变量,用来支持一组相关的功能
命名空间可以在多个文件中定义,并且允许每个文件都定义在一个地方,维护更方便
namespace Shapes {
export namespace Polygons {
export class Triangle {}
export class Square {
length: number;
width: number;
height: number;
constructor(length: number, width: number, height: number) {
this.length = length;
this.width = width;
this.height = height;
}
result() {
return this.length * this.width * this.height;
}
}
export class Circle {}
}
}
const Triangle = new Shapes.Polygons.Triangle();
const Square = new Shapes.Polygons.Square(10, 15, 30);
Square.result();
const Circle = new Shapes.Polygons.Circle();
§ ts中,类型推论和类型别名的意义是什么?
TypeScript会在没有明确的指定类型的时候推测出一个类型,这就是类型推论,如果没声明变量,没定义类型,也没赋值,这时候TS会推断成any类型可以进行任何操作
使用 type关键字
(可以给一个类型定义一个名字)多用于复合类型
定义类型别名:
type str = string
let sss:str = "xiaoming"
console.log(sss)
定义函数别名:
type fn = () => string;
let ff: fn = () => "xiaoming";
console.log(ff);
定义联合类型别名:
type str = string | number;
let s1: str = 123;
let s2: str = "123";
console.log(s1, s2);
定义值的别名
type value = boolean | 0 | "213";
let s: value = true;
console.log(s);