TS高级开发技巧15条
1.可选链 Optional Chaining (?.):
在不用担心遇到 null 或 undefined 值的前提下,可选链允许你安全访问 nested 对象属性和方法。 他会简单计算类型为 null 或者 undefined 的中间属性。
const user = {
name: 'John',
address: {
city: 'New York',
postalCode: '12345'
}
};
const postalCode = user.address?.postalCode;
console.log(postalCode); // Output: 12345
const invalidCode = user.address?.postalCode?.toLowerCase();
console.log(invalidCode); // Output: undefined
2.无效合并操作符 (??):
当变量为 null 或 undefined 时为新变量提供一个默认值
const name = null;
const defaultName = name ?? 'Unknown';
console.log(defaultName); // Output: Unknown
const age = 0;
const defaultAge = age ?? 18;
console.log(defaultAge); // Output: 0
3.类型断言:
当ts无法推断一个变量类型的时候,类型断言允许你显式定义一个变量的类型
const userInput: unknown = 'Hello World';
const strLength = (userInput as string).length;
console.log(strLength); // Output: 11
4.泛型:
泛型能够让你创建可与多种类型一起使用的可重用组件。
// 使用 Generics 的函数示例
function identity<T>(arg: T): T {
return arg;
}
let result = identity<string>("Hello, TypeScript!"); // 类型参数为 string
console.log(result); // 输出:Hello, TypeScript!
let anotherResult = identity<number>(42); // 类型参数为 number
console.log(anotherResult); // 输出:42
// 使用 Generics 的类示例
class Container<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
}
let container = new Container<number>(42); // 类型参数为 number
console.log(container.getValue()); // 输出:42
5.keyof 操作符:
keyof 操作符会返回一个类型所有已知属性的联合类型
interface Person {
name: string;
age: number;
location: string;
}
type PersonKeys = keyof Person;
// PersonKeys 的类型为 "name" | "age" | "location"
6.类型保护:
类型保护用于在编写代码时判断变量的类型,并在特定条件下缩小变量的类型范围
function logMessage(message: string | number) {
if (typeof message === 'string') {
console.log('Message: ' + message.toUpperCase());
} else {
console.log('Value: ' + message.toFixed(2));
}
}
logMessage('hello'); // Output: Message: HELLO
logMessage(3.14159); // Output: Value: 3.14
7.交叉类型:
通过使用交叉类型,我们可以将多个类型合并为一个类型,以获得所有类型的联合。这意味着新类型将具备所有交叉类型中的属性和方法,并且可以在代码中使用这些属性和方法。
interface A {
propA: string;
methodA(): void;
}
interface B {
propB: number;
methodB(): void;
}
type AB = A & B;
const obj: AB = {
propA: "Hello",
propB: 42,
methodA() {
console.log("Method A");
},
methodB() {
console.log("Method B");
}
};
8.映射类型:
是一种用于从现有类型创建新类型的机制。它允许我们基于现有类型的属性进行转换和修改,从而生成具有相似结构但具有不同类型或属性修饰符的新类型。
TypeScript提供了几种Mapped Types的内置形式,包括:
- Partial: 将类型T的所有属性变为可选属性。
- Readonly: 将类型T的所有属性变为只读属性。
- Required: 将类型T的所有可选属性变为必需属性。
- Pick<T, K>: 从类型T中选择指定属性K组成新类型。
- Omit<T, K>: 从类型T中排除指定属性K组成新类型。
- Record<K, T>: 创建一个包含属性K和对应类型为T的新类型。
interface Person {
name: string;
age: number;
}
type PartialPerson = Partial<Person>;
// 等同于 { name?: string; age?: number; }
type ReadonlyPerson = Readonly<Person>;
// 等同于 { readonly name: string; readonly age: number; }
type RequiredPerson = Required<Person>;
// 等同于 { name: string; age: number; }
type PickName = Pick<Person, 'name'>;
// 等同于 { name: string; }
type OmitAge = Omit<Person, 'age'>;
// 等同于 { name: string; }
type RecordExample = Record<'id' | 'name', number>;
// 等同于 { id: number; name: number; }
9. 字符串字面量类型 和 联合类型 :
字符串字面量类型是指将特定字符串直接作为类型的一部分,而不仅仅是字符串值。通过使用字符串字面量类型,我们可以将变量或参数的类型限定为特定的字符串取值之一.
type Color = "red" | "green" | "blue";
let color: Color;
color = "red"; // 合法
color = "green"; // 合法
color = "yellow"; // 不合法,不是 Color 类型的取值之一
联合类型是指将多个类型组合成一个类型的能力。通过使用联合类型,我们可以指定变量或参数的类型可以是多个类型之一
type MyNumber = number | string;
let value: MyNumber;
value = 10; // 合法
value = "Hello"; // 合法
value = true; // 不合法,不是 MyNumber 类型的取值之一
10.装饰器:
用于添加元数据和修改类、方法、属性等声明的特殊语法。装饰器提供了一种简洁的方式来扩展和修改现有的代码,以实现元编程的能力。
function uppercase(target: any, propertyKey: string) {
let value = target[propertyKey];
const getter = () => value;
const setter = (newValue: string) => {
value = newValue.toUpperCase();
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
class Person {
@uppercase
name: string;
}
const person = new Person();
person.name = 'John Doe';
console.log(person.name); // Output: JOHN DOE
11.索引签名:
是 TypeScript 中用于描述对象的动态属性的一种方式。它允许我们定义对象的属性,其名称不是提前定义的,而是根据运行时的索引值动态确定。
interface MyObject {
[index: string]: number;
}
const obj: MyObject = {
age: 25,
score: 90,
};
console.log(obj.age); // 输出:25
console.log(obj.score); // 输出:90
12.带有条件语句的类型推断:
在条件语句中根据代码逻辑进行类型推断的能力。当条件语句中包含逻辑分支,每个分支都有不同的代码路径时,TypeScript 可以根据条件的真假情况推断出变量的类型。
function processValue(value: string | number) {
if (typeof value === "string") {
// 在此分支中,value 的类型被推断为 string
console.log(value.length);
} else {
// 在此分支中,value 的类型被推断为 number
console.log(value.toFixed(2));
}
}
13.只读属性:
用于指定对象属性为只读的特性。一旦属性被标记为只读,就不能再修改该属性的值。
interface Person {
readonly name: string;
readonly age: number;
}
const person: Person = {
name: "Alice",
age: 30,
};
person.name = "Bob"; // 错误!无法修改只读属性
14.类型别名:
用于给现有类型创建一个新名字的特性。它允许我们为复杂或冗长的类型定义创建一个简洁、可重用的名称,以提高代码的可读性和可维护性。
type Point = {
x: number;
y: number;
};
type Shape = 'circle' | 'square' | 'triangle';
function draw(shape: Shape, position: Point) {
console.log(`Drawing a ${shape} at (${position.x}, ${position.y})`);
}
const startPoint: Point = { x: 10, y: 20 };
draw('circle', startPoint); // Output: Drawing a circle at (10, 20)
15.类中的类型守护:
TypeScript 中使用类型守卫来确定类实例的具体类型。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sound(): void {
console.log("The animal makes a sound.");
}
}
class Cat extends Animal {
meow(): void {
console.log("Meow!");
}
}
class Dog extends Animal {
bark(): void {
console.log("Woof!");
}
}
function makeSound(animal: Animal): void {
animal.sound();
if (animal instanceof Cat) {
animal.meow(); // 类型守卫:animal 的类型被确定为 Cat
} else if (animal instanceof Dog) {
animal.bark(); // 类型守卫:animal 的类型被确定为 Dog
}
}
const cat = new Cat("Whiskers");
const dog = new Dog("Buddy");
makeSound(cat); // 输出:The animal makes a sound. Meow!
makeSound(dog); // 输出:The animal makes a sound. Woof!