TypeScript实用技巧

本文介绍了TypeScript中的is关键字、可辨识联合类型、typeof操作、keyof与条件类型,以及常用工具类型如Partial、Required等。还探讨了TSv3.7的新特性,包括可选链、空值合并运算符和断言签名,帮助开发者更好地理解和利用TypeScript的强类型特性来提高代码质量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. is关键字

is关键字可用来判断并限定值的类型,比如:

function user0(name: any): name is string {
  return name === 'lili'
}
function user1(name: any): boolean { 
  return name === 'lili'
}
function getUserName(name: string | number) {
  if(user0(name)) {
    console.log(name)
    console.log(name.length)
    /**
     * 若换成user1(name) boolean就会报错
     * Property 'length' does not exist on type 'string | number'.
     * Property 'length' does not exist on type 'number'.
     */
  }
}
getUserName('lili')

is关键字和boolean的区别大概就在于is可以限定值的类型,类似于boolean+类型断言的作用

2. 可辨识联合类型

为可辨识的特征创建适合的联合类型

interface Info {
    username: string
}
// bad
interface UserAction {
    id?: number
    action: 'create' | 'delete'
    info: Info
}

// good
type UserAction = {
    id: number
    action: 'delete'
    info: Info
} |
{
    action: 'create'
    info: Info
}
const UserReducer = (userAction: UserAction) => {
    switch (userAction.action) {
        case 'delete':
            console.log(userAction.id);
            break;
        default:
            break;
    }
}

3. typeof

通过typeof可以得到某个值的类型,比如:

/** bad */ 
interface Opt {
  timeout: number
}
const defaultOption: Opt = {
  timeout: 500
}

/** good */ 
const defaultOption = {
  timeout: 500
}
type Opt = typeof defaultOption

当一个对象具有字面量初始值时,可以考虑这种写法以减少重复代码

4. keyof & condition type

  • keyof可获取类型上的键值,比如:
type Person = {
  id: number;
  name: string;
}
// 等价于type keys = "id" | "name"
type keys = keyof Person;
  • 条件类型(condition type)可用于判断类型T是否扩展于U

T extends U ? X : Y

比如:

type Exclude<T, U> = T extends U ? never : T;
// 等价于type T = 'b'
type T = Exclude<'a' | 'b' | 'c', 'a' | 'c'>

当T为联合类型的时候,它会自动分发条件。

5. 常用工具类型

  • Partial & Required & Pick
type Partial<T> = {
  [P in keyof T]?: T[P];
};

type Required<T> = {
  [P in keyof T]-?: T[P];
};

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

interface User {
  id: number;
  age: number;
  name: string;
};

// 相当于: type PartialUser = { id?: number; age?: number; name?: string; }
type PartialUser = Partial<User>

// 相当于: type PickUser = { id: number; age: number; }
type PickUser = Pick<User, "id" | "age">

+ -这两个关键字用于映射类型中给属性添加修饰符,比如-?就代表将可选属性变为必选

  • Omit

Omit<T, K>的作用是忽略T中的K属性.

type Omit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; }
// 等价于type Foo = { age: number }
type Foo = Omit<{name: string, age: number}, 'name'>
  • Record

Record<K, T>可将K中所有属性的值转化为T类型.

type Record<K extends string | number | symbol, T> = { [P in K]: T; }
enum AnimalType {
  CAT = 'cat',
  DOG = 'dog',
  FROG = 'frog',
}
interface AnimalDescription { 
  name: string, 
  icon: string 
}
const AnimalMap: Record<AnimalType, AnimalDescription> = {
  cat: { name: '猫', icon: '🐱'},
  dog: { name: '狗', icon: '🐶' },
  frog: { name: '蛙', icon: '🐸' },
};
  • ReturnType

ReturnType可用来获取函数的返回类型

type ReturnType<T extends (...args: any) => any> = T extends (
  ...args: any
) => infer R 
  ? R :
  any;
  
function foo(x: number): Array<number> {
  return [x];
}
// 等价于type fn = number[]
type fn = ReturnType<typeof foo>;

在条件类型语句中, infer关键字可声明一个类型变量并对它进行使用,这里是判断T如果为( ...args: any[] ) => infer R函数类型的子类型,则返回R,否则返回any.


tsv3.7特性

1. 可选链

通过可选属性访问操作符?.可选择性的对数据进行访问,通过这种方式,如果存在尚未定义的父级对象(null或undefined),则会在链中的任何位置返回undefined,而不是在运行时崩溃.

// before
if(data && data.customer && data.customer.address){
 const {address} = data.customer;
 ....
}
// now
const address = data?.customer?.address;
// 也适用于数组
customer?.[0]?.['address']
// 检查方法是否已定义并调用
customer.approve?.()

?.的行为与使用&&实现的代码不同,因为&&专门用于“假值”(空字符串、0、NaN、false)的判断,但?.却不会在遇到有效数据(如:0和空字符等)的时候短路.

2. 空值合并

如果左侧是nullundefined,空值合并运算符??会返回右侧的表达式.

// before
const x = data || 'defaultValue';
// now
const x = data ?? 'defaultValue';

同样,相比||运算符,??可排除有效数据如0、false等的短路.

3. 断言签名

断言签名可对node.js assert功能进行模拟,或者不检查条件而告诉ts特定的变量具有不同的类型.

function assert(condition: any, msg?: string): asserts condition {
    if (!condition) {
        throw new AssertionError(msg)
    }
}
function assertIsString(val: any): asserts val is string {
    if (typeof val !== "string") {
        throw new AssertionError("Not a string!");
    }
}

更多特性可翻阅Roadmap

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值