什么是TypeScript?
是一种编程语言,是JavaScript的超集,过添加静态类型
、类
、接口
和模块
等功能,使得在大型应用程序中更容易进行维护和扩展,可以编译成纯JavaScript
静态类型和动态类型有什么区别?
静态类型 | 动态类型 | |
定义 | 在编译阶段进行类型检查的类型系统,通过类型注解或推断来确定变量、参数和返回值的类型。 | 在运行时才确定变量的类型,允许同一个变量在不同时间引用不同类型的值 |
特点 | 在编码阶段就发现大部分类型错误 | 运行时可以根据上下文或条件动态地改变变量的类型 |
优势 | 可以在编辑器或 IDE 中实现代码提示、自动补全和类型检查,帮助开发者减少错误并提高代码质量 | 更大的灵活性,快速,适用于一些需要频繁变化类型的场景 |
类型断言是什么?
显式告诉编译器变量的类型,使用 <type> 或 as type 语法实现的
let strLength: number = (someValue as string).length;
let strLength: number = (<string>someValue).length;
泛型是什么?
允许创建可与各种类型一起使用的可重用组件或函数
function identity<T>(arg: T): T {return arg;
}
const result1 = identity<number>(42); // Explicitly specifying the type
const result2 = identity('hello'); // Inferring the type
"keyof"、"typeof"、 "in"、"infer"关键字是什么?
- keyof:用于获取对象类型的键的并集
- typeof:用于获取值的类型
- in:检查属性键是否存在于从“keyof”获得的键的并集中
- infer:从条件类型中的另一种类型推断出类型
interface Person {name: string;age: number;
}
type PersonKeys = keyof Person; // Result: "name" | "age"
const john = { name: 'John', age: 30 };
type JohnType = typeof john; // Result: { name: string, age: number }
type CheckKey<T, K extends keyof T> = K extends 'name' ? true : false;
interface Person {name: string;age: number;
}
type IsNameKey = CheckKey<Person, 'name'>; // Result: true
type IsCityKey = CheckKey<Person, 'city'>; // Result: false
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function add(a: number, b: number): number {return a + b;
}
type AddReturnType = ReturnType<typeof add>; // Result: number
什么是条件类型?
允许创建依赖于条件的类型,用于根据类型之间的关系执行类型推断
type IsString<T> = T extends string ? true : false;
type CheckString = IsString<string>; // Result: true
type CheckNumber = IsString<number>; // Result: false
什么是映射类型?
允许通过将属性映射到新类型来基于现有类型创建新类型
interface Person {name: string;age: number;
}
type PersonWithOptionalProperties = { [K in keyof Person]?: Person[K] };
const john: Person = { name: 'John', age: 30 };
const johnWithOptionalProperties: PersonWithOptionalProperties = { name: 'John' };
什么是条件映射类型?
将条件类型和映射类型结合起来,根据条件执行类型转换,根据现有类型的属性创建动态类型
interface Person {name: string;age: number;
}
type MappedConditional<T> = {[K in keyof T]: T[K] extends number ? string : T[K];
};
const john: MappedConditional<Person> = { name: 'John', age: '30' };
"mixins"是什么?
将某个类与一个或多个其他类组合来向该类添加行为
class Printable {print() {console.log(this.toString());}
}
class MyObject {constructor(private name: string) {}toString() {return `Object: ${this.name}`;}
}
interface MyObject extends Printable {}
const myObj = new MyObject('example');
myObj.print(); // Output: "Object: example"
Printable 类充当 mixin,将 print 方法添加到 MyObject 类
"装饰器"是什么?
允许、修改类、方法或属性的行为,使用 @decoratorName 语法声明并在运行时执行
function MyClassDecorator<T extends { new (...args: any[]): {} }>(constructor: T) {return class extends constructor {newProperty = 'decorated property';hello = 'overridden';};
}
@MyClassDecorator
class MyClass {hello: string;constructor() {this.hello = 'world';}
}
const myClassInstance = new MyClass();
console.log(myClassInstance.hello); // Output: "overridden"
console.log((myClassInstance as any).newProperty); // Output: "decorated property"
”键重新映射“、"值重新映射"是什么?
interface Person {name: string;age: number;
}
// 键重新映射
type MappedPerson = { [K in keyof Person as `new_${K}`]: Person[K] };
const john: MappedPerson = { new_name: 'John', new_age: 30 };// 值重新映射
type ValueRemapped<T> = T extends 'a' ? 'x' : T extends 'b' ? 'y' : 'z';
type Result = ValueRemapped<'a' | 'b' | 'c'>; // Result: 'x' | 'y' | 'z'
"Pick"、"Omit"、"Exclude"、"部分"、"只读"实用程序类型是什么?
- Pick:通过从现有类型中选择特定属性来创建新类型
- Omit:通过从现有类型中排除特定属性来创建新类型
- Exclude: 通过从联合中排除某些类型来创建新类型
- 部分: 使现有类型的所有属性成为可选
- 只读:使现有类型的所有属性变为只读
interface Person {name: string;age: number;city: string;
}// Pick
type PersonInfo = Pick<Person, 'name' | 'age'>;
const john: PersonInfo = { name: 'John', age: 30 };// Omit
type PersonWithoutCity = Omit<Person, 'city'>;
const john: PersonWithoutCity = { name: 'John', age: 30 };// Exclude
type Color = 'red' | 'green' | 'blue';
type PrimaryColors = Exclude<Color, 'green' | 'blue'>;
const primary: PrimaryColors = 'red'; // Okay
const invalidColor: PrimaryColors = 'green'; // Error: Type '"green"' is not assignable to type 'PrimaryColors'.// 可选
interface Person {name: string;age: number;
}
type PartialPerson = Partial<Person>;
const john: PartialPerson = { name: 'John' };// 只读
interface Person {readonly name: string;age: number;
}
const john: Readonly<Person> = { name: 'John', age: 30 };
john.age = 31; // Error: Cannot assign to 'age' because it is a read-only property.
"模板文字类型"是什么?
使用模板文字语法来操作类型中的字符串
type Greeting<T extends string> = `Hello, ${T}!`;
type GreetJohn = Greeting<'John'>; // Result: "Hello, John!"
type GreetJane = Greeting<'Jane'>; // Result: "Hello, Jane!"
"keyof T extends K"构造是什么?
type FilterProperties<T, K> = {[P in keyof T as T[P] extends K ? P : never]: T[P];
};
interface Person {name: string;age: number;email: string;
}
type StringProperties = FilterProperties<Person, string>;
// Result: {
// name: string;
// email: string;
// }
type NumberProperties = FilterProperties<Person, number>;
// Result: {
// age: number;
// }
"私有"、”受保护“访问修饰符是什么?
私有:Private
受保护:protected
类型
基本类型
类型 | 使用 |
布尔 | |
数字 | |
字符串 | |
Null | |
Undefined | |
复合类型
数组 | |
元组Tuple | |
枚举enum | |
对象类型
object | 非原始类型 |
interface | 描述对象结构,可复用 |
函数类型
任意值any | |
空值void | |
Never | 会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型 |
高级类型
交叉类型
interface A {a(): void;
}
interface B {b(): void;
}
type C = A & B; // 表示同时具备 A 和 B 的特性
联合类型
let myVar: string | number;
myVar = "Hello"; // 合法
myVar = 123; // 合法
泛型generic
定义:在定义函数、类或接口时使用类型参数的方式,以增加代码的灵活性和重用性
泛型变量
function loggingIdentity<T>(arg: T[]): T[] {console.log(arg.length); // Array has a .length, so no more errorreturn arg;
}function loggingIdentity<T>(arg: Array<T>): Array<T> {console.log(arg.length); // Array has a .length, so no more errorreturn arg;
}
泛型函数loggingIdentity,接收类型参数T和参数arg(它是个元素类型是T的数组),并返回元素类型是T的数组
泛型类型
interface GenericIdentityFn<T> {(arg: T): T;
}function identity<T>(arg: T): T {return arg;
}let myIdentity: GenericIdentityFn<number> = identity;
泛型类
class GenericNumber<T> {zeroValue: T;add: (x: T, y: T) => T;
}let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
泛型约束 extends
对类型参数做一定的限制
// extends对传入的参数做了一个限制,就是entities的每一项可以是一个对象,但是必须含有类型为string的cname属性
function getCnames<T extends { name: string }>(entities: T[]):string[] {return entities.map(entity => entity.cname)
}
枚举enum
定义:对数字值集合进行命名的方式,增加代码可读性
enum FileAccess {// constant membersNone,Read = 1 << 1,Write = 1 << 2,ReadWrite = Read | Write,// computed memberG = "123".length
}
枚举和常量枚举的区别:
枚举
可以包含计算得出的值,而常量枚举则在编译阶段被删除,并且不能包含计算得出的值,它只能包含常量成员。
常量枚举
在编译后会被删除,而普通枚举会生成真实的对象。
类型声明和类型推断的区别
// 类型声明:显示为变量或者函数指定类型
let x: number;
x = 10;
// 类型推断:根据语句右侧的值自动推断类型
let y = 20; // TypeScript会自动推断y的类型为number
可控类型和undefined类型使用
let numberOrNull: number | null = 10;
numberOrNull = null; // 可以赋值为null let stringOrUndefined: string | undefined = "Hello";
stringOrUndefined = undefined; // 可以赋值为undefined
接口interface
用于描述对象的形状的结构化类型。它定义了对象应该包含哪些属性和方法。在TypeScript中,接口可以用来约束对象的结构,以提高代码的可读性和维护性。
接口和类型别名的区别:
- 接口定义了一个契约,描述了对象的形状(属性和方法),以便在多个地方共享。它可以被类、对象和函数实现。
- 类型别名给一个类型起了一个新名字,便于在多处使用。它可以用于原始值、联合类型、交叉类型等。与接口不同,类型别名可以用于原始类型、联合类型、交叉类型等,而且还可以为任意类型指定名字。
类型 | 使用 |
可选属性 | |
只读属性 | 只读,不可更改 |
函数类型 | |
可索引的类型 | 有两种索引签名:字符串和数字 |
类类型 | |
类静态与实例 | |
继承(单、多个) | |
混合类型 | |
接口继承类 |
命名空间(Namespace)和模块(Module)
命名空间 | 模块 | |
定义 | 将具有相似功能或者属性的类、接口进行分组,避免全局命名冲突 | 将相关代码组织到单独的文件,使用module来导入、导出这些文件中的功能 |
作用 | 全局范围内组织代码的方式,防止命名冲突 | 组织代码,多个文件共享代码 |
示例 |
类型守卫(Type Guards)
在特定的作用域内缩小变量的范围,以确保正确推断类型。
索引类型(Index Types)
创建具有动态属性名称的对象,并且能够根据已知的键来获取相应的属性类型
优点:
1、动态属性访问
在处理动态属性名的对象时,可以使用索引类型来实现类型安全的属性访问。例如,当从服务器返回的 JSON 数据中提取属性时,可以利用索引类型来确保属性名存在并获取其对应的类型。
2、代码重用
当需要创建通用函数来操作对象属性时,索引类型可以帮助我们实现更加通用和灵活的代码。例如,一个通用的函数可能需要根据传入的属性名称获取属性值,并进行特定的处理
3、动态扩展对象
当需要处理来自外部来源(比如 API 响应或数据库查询)的动态数据时,索引类型可以让我们轻松地处理这种情况,而不必为每个可能的属性手动定义类型。
4、类型安全性
索引类型可以增强代码的类型安全性,因为它们可以捕获可能的属性名拼写错误或键不存在的情况。
5、映射类型
TypeScript 还提供了映射类型(Mapped Types)的概念,它们利用索引类型可以根据现有类型自动生成新类型。这在创建新类型时非常有用,特别是当需要在现有类型的基础上添加或修改属性时。
const、readonly的区别
- const:声明常量值,赋值后,不可修改,常用于常数或固定值
- readonly:在声明时或者构造函数中,被赋值,赋值后,不能修改;通常用在表示对象的某些属性是只读,防止外部修改
const PI = 3.14;
PI = 3.14159; // Error: 无法重新分配常量class Person {readonly name: string;constructor(name: string) {this.name = name; // 可以在构造函数中赋值}
}let person = new Person("Alice");
person.name = "Bob"; // Error: 无法分配到"name",因为它是只读属性
any类型的作用,缺点
允许编写代码时不指定具体的类型,从而可以接受任何类型的值,放弃了静态类型检查
使用场景
- 不确定变量或者表达式具体类型,可用any
- 与动态类型的Js代码交互时,可用来出来这些动态类型的值
缺点
1、降低可读性
let data: any;
// 代码中的使用方式
data.someUnknownMethod(); // 在编译阶段不会报错,但实际上可能是一个错误
2、可能会导致编译不报错,运行时错误
let myVariable: any = 123;
myVariable.toUpperCase(); // 在编译阶段不会报错,但在运行时会引发错误
3、类型安全受损
function add(x: any, y: any): any {return x + y; // 编译器无法推断返回值的具体类型
}