首页 > 解决方案 > 打字稿将缺少的字段添加到应该具有它们的类型

问题描述

我正在尝试为我的数据库代码创建一个辅助函数。数据库中的每个表都有一些公共字段(pk、创建日期等),这些字段被实现为其他表的接口扩展的公共接口。我想创建一个函数,自动将这些公共属性添加到对象并返回新对象,但是打字稿以我无法理解的方式抱怨子类型分配......

Type '{ id: string; } & Pick<T, Exclude<keyof T, "id">>' is not assignable to type 'T'.
  '{ id: string; } & Pick<T, Exclude<keyof T, "id">>' is assignable to the constraint of type 
'T', but 'T' could be instantiated with a different subtype of constraint 'Common'

我在 TS 操场上做了一个最小的代码示例来重现它。对于给定类型,T 扩展了 Common 基接口,我想接受一个包含 T 的所有必需字段的对象,除了Common 中的那些,然后返回带有添加字段的对象。

interface Common {
    id: string;
}

interface Table extends Common {
    data: string;
}

function makeDbEntry<T extends Common>(initial: Omit<T, 'id'>): T {
    const common: Common = {
        id: 'random string',
    };
    const ret: T = {
        ...common,
        ...initial,
    };
    return ret;
}

游乐场链接

据我所知,似乎没有任何方法可以在不明确列出其字段的情况下从 T 中删除字段(不能执行“T 减去 Common”)...

标签: typescript

解决方案


如果条件类型包含类型参数,则无法完全解析它们。这意味着,至少对于这个用例来说,Omit<T, 'id'>基本上是不透明的,TS 看不到这个类型是什么,它只是一个不同于T.

Omit<T, 'id'>当您进行展开操作时,TS 正确理解这将在 common 和( { id: string; } & Omit<T, 'id'>)之间产生交集。但是因为Omit正如我所说的那样不透明,所以无法对这种类型进行进一步的简化。尽管对人类来说这很容易解决T,但 typescript 不知道如何简化这种类型。

如果替换T为具体类型,则 ts 可以很容易地理解 example{ id: string; } & Omit<Table, 'id'>与 相同Table,因此可以使用:

function makeDbEntry<T extends Common>(initial: Omit<T, 'id'>): T {
    const common: Common = {
        id: 'random string',
    };
    const ret: T = {
        ...common,
        ...initial,
    } as T;
    return ret;
}

对于通用版本,最好的选择是使用类型断言,因为在大多数情况下,开发人员比编译器有更好的理解:

function makeDbEntryForTable(initial: Omit<Table, 'id'>): Table {
    const common: Common = {
        id: 'random string',
    };
    const ret: Table = {
        ...common,
        ...initial,
    };
    return ret;
}

游乐场链接


推荐阅读