输入按名称附加字段的安全方法

Type safe way to append field by name

正在尝试实现函数,该函数接收对象 O、字段名称 N 和字段值 T returns 对象,该对象具有来自 [=11= 的所有字段],以及名称为 N 且类型为 T

的字段

试图实现这样的方式:

export function immutableAdd<O, N extends string, T>(object: O, name: N, value: T): O & { [key: N]: T } {
    return { ...object, [name]: value };
}

let a = { a: 3 };
let b = immutableAdd(a, 'test', 'value');
// `b` should have type { a: number, test: string } here
let c = b.test;
c === 'value'

但是这段代码不起作用,因为这部分有错误:{ [key: N]: T }(索引签名参数类型必须是'string'或'number')

这个函数的return类型怎么写?

建议的 return 类型是这样的:

O & {[key : string] : T}

或者这个:

O & {[key : number] : T}

好像字段名称的类型只能是字符串或者数字

我认为,您在这里有两个选择:a) 为 immutableAdd return 类型添加类型断言或 b) 使用 Record 类型。如果您可以稍微更改函数签名,我更喜欢 b),因为它更 concise/enforces 更强的打字。

a) Type assertion

export function immutableAdd<O, N extends string, T>(
    object: O, name: N, value: T): O & { [key in N]: T } {
    // or create separate const to assert the added property type only; like:
    // const prop = { [name]: value } as { [key in N]: T }    
    return { ...object, [name]: value } as O & { [key in N]: T }
}

let b = immutableAdd({ a: 3 }, 'test', 'value'); // {a: number; } & { test: string; }

注意:我用了一个mapped type { [key in N]: T } as part of the return type to include the name property. { [key: N]: T } (what you used) indicates an index signature,它只能有stringnumber类型。在你的例子中,N 是一个带有约束 extends string 的类型参数,而不是 string,因此是编译器错误。

return { ...object, [name]: value } 将解析为类型 O & { [x: string]: T; },因为 name 的确切字符串文字类型是未知的(我们只知道它扩展了 string)并且因此不能分配给 O & { [key in N]: T },所以我们投射。

b) Record 类型

export function immutableAdd<O, N extends string, T>(
    object: O, property: Record<N,T>): O & { [key in N]: T } {
    return { ...object, ...property };
}

let b = immutableAdd( { a: 3 }, { test: 'value' }); // { a: number; } & { test: string; }