【问题标题】:How to assign fixed type value to partial of generic constrained type?如何将固定类型值分配给部分通用约束类型?
【发布时间】:2021-04-09 06:48:24
【问题描述】:

我有一个基本接口,用于处理可能有 Id 的事物:

interface Identifiable {
    id?: number;
}

我有一个通用函数,可以将记录对象转换为具有 id 的事物:

function fromRowToObj1<T extends Identifiable>(row: { id: number; }): Partial<T> {
    return { id: row.id };
    // Type '{ id: number; }' is not assignable to type 'Partial<T>'.
}

我知道发生这种情况是因为有 Ts 扩展了 Identifiable,这会使 return 语句非法。例如,{ id: undefined }{ id: 1 } 类型。所以我决定稍微调整返回类型以强制使用数字 id:


type Identified<T extends Identifiable> = {
    [K in keyof T]?: K extends "id" ? number : T[K];
}
// Should give something like type C = { id?: number | undefined; ... }

function fromRowToObj2<T extends Identifiable>(row: { id: number; }): Identified<T> {
    return { id: row.id };
    // Type '{ id: number; }' is not assignable to type 'Identified<T>'.
}

为什么?哪个可能的T(例如T extends Identifiable)使得{ id: number } 不能分配给Identified&lt;T&gt;

如果无法调整Identified 类型以使其工作,是否有另一种方法可以键入转换函数以使用Identifiable 的泛型子类型?

Link to playground.

【问题讨论】:

    标签: typescript


    【解决方案1】:

    您所面临的问题已在here 进行了详尽的描述。正如您注意到的那样,T extends Identifiable 的子类型会使您的返回值 { id: row.id } 无效。例如Identified&lt;{id?: never}&gt; 永远不会对{ id: row.id } 有效。 Never 仍然是 id 的有效类型,因为您将 Identified 的所有键声明为可选。如果T extends IdentifiableIdentified&lt;T&gt; 实际上等于Partial&lt;T&gt;。 Typescript 在这里正确地抛出了一个错误。不过,如果您设置有效的默认值,您仍然可以解决这个问题(playground):

    interface Identifiable {
        id?: number;
    }
    
    // results in optional id
    function fromRowToObj1<T extends Identifiable>(row: { id: number; }) {
        const result: Partial<T> = {} // valid for all subtypes of Partial<T>
        result.id = row.id
        return result;
    }
    
    // results in non optional id
    function fromRowToObj2<T extends Identifiable>(row: { id: number; } ) {
        const partial: Partial<T> = {}; // valid for all subtypes of Partial<T>
        const result = {
            ...partial,
            id: row.id
        };
        return result;
    }
    
    interface TestObject {
        id: number,
        arg1: string;
        arg2: boolean;
    }
    
    const result1 = fromRowToObj1<TestObject>({id: 5});
    result1.id // optional
    result1.arg1 = "test" // intellisense works
    result1.arg2 = true; // intellisense works
    
    const result2 = fromRowToObj2<TestObject>({id: 5});
    result2.id // not optional
    result2.arg1 = "test" // intellisense works
    result2.arg2 = true; // intellisense works
    

    【讨论】:

    • 只要 Identifiable 中的 id 是可选的,这些变通方法就有可能破坏它们的类型。我已经在操场上发布了这种类型的示例。但这些类型可能无论如何都不会使用。
    • 操场上说Identifiable&lt;{ id?: never}&gt;{ id?: number | undefined}。那么为什么{ id: number } 不能分配给它呢?
    • 无论如何,您的第二个带有非可选 id 的示例效果很好。有了它,我可以将返回类型输入为Partial&lt;T&gt; &amp; { id: number },这就是我想要的。必须通过那个箍确实感觉有点奇怪。
    猜你喜欢
    • 2014-01-04
    • 1970-01-01
    • 1970-01-01
    • 2016-12-30
    • 1970-01-01
    • 1970-01-01
    • 2021-04-23
    • 1970-01-01
    • 2021-05-22
    相关资源
    最近更新 更多