TypeScript实现一个将类型中部分属性修改为可选属性的方法

avatar

背景

在使用TS开发项目的时候,有一些比较实际的需求,比如:

我现在有一个创建文档的方法,如下:

interface Document {
    title:string;
    content:string;
    status:number;
    author:string;
    date:string;
    readCount:number;
}
interface DocumentOptions{
    title:string;
    content:string;
    status?:number;
    author?:string;
    date?:string;
    readCount?:number;
}
function createDocument(options:DocumentOptions): Article {
    let article = {} as Document;
    article.title = options.title;
    article.content = options.content;
    article.status = options.status || 0;
    article.author = options.author || 'admin';
    article.date = options.date || new Date().toISOString();
    article.readCount = options.readCount || 0;
    return article;
}

可以看到Document和DocumentOptions属性内容相同,只有一些必填非必填的区别,我在想有没有一种方法让我可以在Document 的基础上把一些字段转为非必填呢?像下面这样:

interface Document {
    title:string;
    content:string;
    status:number;
    author:string;
    date:string;
    readCount:number;
}
type DocumentOption = CreateDocumentOption<Document, 'status'|'author'|'date'|'readCount'>;
// DocumentOption得到的结果如下
type DocumentOption = {
    title:string;
    content:string;
    status?:number;
    author?:string;
    date?:string;
    readCount?:number;
}

遗憾的是TS并没有给我们提供这样的方法,我们只能自己写一个方法实现这个功能了,实现结果如下:

type CreateDocumentOptions<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
// 使用
type DocumentOption = CreateDocumentOption<Document, 'status'|'author'|'date'|'readCount'>;

关键词解释

&

  • & 符号表示交叉类型(Intersection Types)。交叉类型允许将多个类型合并为一个类型,该类型将具有所有类型的属性和方法。例如:
interface A {
    a: string;
}
interface B {
    b: string;
}
type C = A & B;
// C的结果
type C = {
  a: string;
  b: string;
}

Omit

  • Omit 是一个内置的类型工具,用于从对象类型中排除指定的属性。 例如:
interface D {
    d: string;
    e: string;
    f: string;
}
type E = Omit<D, 'd' | 'e'>;
// E的结果
type E = {
  f: string;
}

Pick

  • Pick 是一个内置的类型工具,用于从对象类型中选择指定的属性创建新的类型。例如:
 interface F {
    f: string;
    g: string;
    h: string;
}
type G = Pick<F, 'f' | 'g'>;
// G的结果
type G = {
   f: string;
   g: string;
}

Partial

  • Partial 是一个内置的类型工具,用于将对象类型中的所有属性变为可选属性。例如:
interface H {
    h: string;
    i: string;
    j: string;
}
type I = Partial<H>;
// I的结果
type I = {
  h?: string;
  i?: string;
  j?: string;
}

通过综合使用&、Omit、Pick、Partial就可以实现将类型中的部分属性修改为可选属性了。