TS不基础使用指南

250 阅读4分钟

断言

断言的作用, 官方的解释

  • 将联合类型断言为其中一种类型
  • 父类断言为更加具体的子类
  • any 断言为具体的类型

更通俗的解释: 类型断言更像是类型的选择, 而不是类型的转换

在实际中, 使用最多的就是在组件中设置了具体的联合类型值, 而在实际使用的时候, 类型推断和组件的类型定义不匹配, 需要手动 断言 一下它的格式

// 报错, 因为val不一定具有length属性
function func(val: string | number) :number {
    if (val.length) {
        return val.length
    } else {
        return val.toString().length
    }
}

// 正确的写法
function func(val: string | number) :number {
    if ((val as string).length ) {
        return (val as string).length
    } else {
        return val.toString().length
    }
}

更符合场景的一个例子

// antd 中的 Table 组件, columns 有一个属性叫做 align, 为AlignType类型, 如果Table经过封装, 传给 align的值可能会被类型推断为string, ts会报错 不能把 string赋值给AlignType, 此时就需要用到断言

interface & type

相同点

  1. 都可以描述对象或者函数
// interface
interface User {
    name: string;
    age: number;
}

interface UpdateUser {
    (name: string, age: number): void;
}

// type
type User = {
    name: string;
    age: number;
}

type UpdateUser = (name: string, age: number): void;

  1. 都支持扩展
// interface
interface User {
    name: string;
    age: number;
}

interface Student extends User {
    grade: number;
    score: number;
}

// type
type User = {
    name: string;
    age: number;
}

type Student = User & {
    grade: number;
    score: number;
}

不同点

  1. interface 支持声明合并
interface User {
    name: string;
}

interface User {
    age: number;
}

// 这里的 User 同时有 name 和 age 两种
const user: User = {
    name: '张三',
    age: '18',
}
  1. type 可以声明基本类型别名、联合类型、元组类型等
// 联合类型
type User1 = {
    name: string;
    age: number;
}

type User2 = {
    name: string;
    score: number;
}

type Person  = User1 | User2;

// 具体定义数组每个位置的类型
type PersonList = [User1, User2];

type size = 'small' | 'middle' | 'large';
  1. type 可以使用 typeof 获取实例的类型
const defaultProps = {
    name: '张三',
    age: 18,
    score: 722,
}

type IProps = typeof defaultProps & {
    favorite: [string];
}

const UserComponent: React.FC<IProps> = (props) => {
    const { name, age, score, favorite } = props;
    return (
        <>
            <p>{name}</p>
            <p>{age}</p>
            <p>{score}</p>
            <p>{favorite.join('、')}</p>
        </>
    )
}

UserComponent.defaultProps = defaultProps;

总结

  1. 官方推荐使用 interface,除非你需要使用到 interface 无法满足,只能使用 type 时再使用 type 来定义类型
  2. 实践中, 除了基本类型的定义, 还需使用到联合类型, 交叉类型等, 更推荐使用 type 类型

typeof keyof

keyof 用于遍历某种类型的属性, 假设有一个类型 T, keyof T 将会给你一个新类型, 这个新类型就是 T 的属性组成的联合类型

typeof 操作符用于获取变量的类型

// 报错  type ‘string’ can’t be used to index type ‘{}’.
function getProp(obj: object, key: string) {
  return obj[key];
}

// 限制 key 为 obj 的属性值
function getProp<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

另一个例子

enum ColorsEnum {
    white = '#ffffff',
    black = '#000000',
}

type Colors = keyof typeof ColorsEnum;

// 等价于
type Colors = "white" | "black"
// 首先 typeof ColorsEnum 会返回 ColorsEnum的类型, 类似于{ name: string, power: string }, keyof操作这个返回值, 得到其中的属性, 即white 和 black

声明文件

声明文件就是以 .d.ts 结尾的文件。常用于对第三方库或者项目中的一些公共类型的声明, 和声明文件紧密关联的是声明语句 declare

声明文件有全局的类型声明和局部的类型声明两种

.d.ts 里面,没有使用 import、export,默认是全局的。全局的类型声明在项目的任何地方都可以直接使用,无需引入。但是要特别注意类型命名冲突。在 .d.ts 文件中,只要有一个类型定义使用了 export,那这个声明文件就会变成模块化的。想要使用里面的类型定义,需要先通过 import 的方式将其引入才行。

总结:

  1. 全局的类型:直接放在最外层的 global.d.ts 或者 typing.d.ts 中,不使用 export 导出。

  2. 模块级的类型。在每个功能模块下,定义一个 index.d.ts 文件。在这个文件中写需要复用的类型定义。再通过 export 的方式将其导出。在需要使用类型的地方,再通过 import 导入使用。

泛型

泛型是对类型的编程

你不知道的 TypeScript 泛型(万字长文,建议收藏)

常见工具类型

Partial, Record<Keys,Type>, Pick<Type, Keys>, Omit<Type, Keys>

Partial作用: 将传入的类型定义转换成全部可选的值

type User = {
    id: number;
    name: string;
}

type UserPartial = Partial<User>;

// 等价于
type UserPartial = {
    id?: number | undefined;
    name?: string  | undefined;
}

Required作用: 将传入的类型定义全部转换在必选的

Record<Keys,Type>作用: 构建一个对象类型,其属性键是 Keys,其属性值是 Type

interface CatInfo {
  age: number;
  breed: string;
}

type CatName = "miffy" | "boris" | "mordred";

const cats: Record<CatName, CatInfo> = {
  miffy: { age: 10, breed: "Persian" },
  boris: { age: 5, breed: "Maine Coon" },
  mordred: { age: 16, breed: "British Shorthair" },
};

Pick<Type, Keys>作用: 通过从 Type 中选择一组属性 Keys(字符串文字或字符串文字的联合)来构造一个类型。

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

Omit<Type, Keys>作用: 排除一个类型下的一个或者几个类型

interface Todo {
  title: string;
  description: string;
  completed: boolean;
  createdAt: number;
}

type TodoInfo = Omit<Todo, "completed" | "createdAt">;

const todoInfo: TodoInfo = {
  title: "Pick up kids",
  description: "Kindergarten closes at 5pm",
};

参考

TypeScript - 简单易懂的 keyof typeof 分析 TS 中的 keyof 和 typeof 操作符 你不知道的 TypeScript 泛型(万字长文,建议收藏)