TS高级开发技巧15条

167 阅读5分钟

TS高级开发技巧15条

image.png image.png image.png

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的内置形式,包括:

  1. Partial: 将类型T的所有属性变为可选属性。
  2. Readonly: 将类型T的所有属性变为只读属性。
  3. Required: 将类型T的所有可选属性变为必需属性。
  4. Pick<T, K>: 从类型T中选择指定属性K组成新类型。
  5. Omit<T, K>: 从类型T中排除指定属性K组成新类型。
  6. 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!