【问题标题】:Typescript: Why would this generic merge fail?Typescript:为什么这个通用合并会失败?
【发布时间】:2018-04-11 13:39:19
【问题描述】:

鉴于 Typescript 2.5.3 中的这些类型...

type Data<T> = {
    [K in keyof T]?: T[K]
}

interface Merge {
    body: {
        isMerged: boolean
    }
}

interface Args {
    one: number
    two: number
}

为什么这个函数会编译失败:

function merge<T>(data: Data<T>): Merge & { body: Data<T> } {
    const merged = {
        body: {
            isMerged: true
        }
    }

    // Written this way to guarantee { body: isMerged } remains intact
    merged.body = Object.assign({}, data, merged.body);

    return merged;
}

然而这个电话……

const m = merge<Args>({
    one: 1,
    two: 2,
});

...正确推断返回的数据?

编辑:

我意识到将返回类型显式转换为:

return <Merge & { body: Data<T> }> merged;

... 抑制错误,但我的问题是为什么 Typescript 不能在以下情况下推断类型:

  • 它知道函数签名中的返回类型
  • 可以看到body中的data没有任何变化

编辑 2 和结论/答案:

根据 Titian Cernicova-Dragomir 的回答,TL;DR 似乎是这样的:最初定义的类型是分配的类型。 类型不会变形。

我期待 Typescript 知道向一个类型添加额外的键将使其最终满足返回类型接口,但似乎最初定义的类型是坚持的类型。

因此,函数应该是:

function merge<T>(data: Data<T>): Merge & { body: Data<T> } {
    const merged ={
        body : Object.assign({}, data, {
            isMerged: true
        })
    };
    return merged;
}

merged 在一个表达式中定义,而不是添加到多个表达式中。

【问题讨论】:

    标签: typescript types


    【解决方案1】:

    问题在于,当您尝试将 const merged 分配给具有 { isMerged: boolean } &amp; Data&lt;T&gt; 主体的对象时,您的 const merged 是使用您设置的主体(仅具有 isMerged 属性的对象)键入的,它会失败,因为无法判断该对象是否满足Data&lt;T&gt; 接口。试试:

    function merge<T>(data: Data<T>): Merge & { body: Data<T> } {
        const merged ={
            body : Object.assign({}, data, {
                isMerged: true
            })
        };
        return merged;
    }
    

    【讨论】:

    • 谢谢,但我不明白为什么 merged = {body: Object.assign({}, data, { isMerged: true })};merged = {body: { isMerged: true }}; merged.body = Object.assign({}, data, merged.body); 在功能上有所不同。你是说合并的常量会得到它第一个定义的类型——即使对象的一部分后来被更新以符合返回形状?
    • 是的,当你声明和赋值一个变量时,除非你明确指定类型注解,否则变量的类型是由赋值决定的。
    • 在这种情况下,如果您声明了const merged : Merge &amp; { body: Data&lt;T&gt; } ,那么在尝试进行初始化时仍然会出错,因为{ isMerged: true } 不满足Data&lt;T&gt; 接口。
    • 我知道如果 data 尚未应用,Merge &amp; { body: Data&lt;T&gt; } 在声明时将不满足;我的问题更多是关于 Typescript 是否足够精明以处理 morphing types 或原始类型合同是否一成不变。我猜是后者。谢谢你启发我。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多