我们正在寻找使用Object.assign的类型安全方式。但是,我们似乎无法使其发挥作用。
要显示我们的问题,我将使用Generics文档中的copyFields方法
function copyFields<T extends U, U>(target: T, source: U): T {
for (let id in source) {
target[id] = source[id];
}
return target;
}
function makesrc(): Source { return {b: 1, c: "a"}}
interface Source {
a?: "a"|"b",
b: number,
c: "a" | "b"
}
我希望引擎阻止我创建未声明的属性
/*1*/copyFields(makesrc(), {d: "d"}); //gives an error
/*2*/copyFields(makesrc(), {a: "d"}); //gives an error
/*3*/copyFields(makesrc(), {c: "d"}); //should give an error, but doesn't because "a"|"b" is a valid subtype of string.
//I don't want to specify all the source properties
/*4*/copyFields(makesrc(), {b: 2}); //will not give me an error
/*5*/copyFields(makesrc(), {a: "b"}); //should not give an error, but does because string? is not a valid subtype of string
我们试图通过明确地为copyfields调用提供类型来解决这个问题 但是我们找不到能使所有例子都有效的电话。
例如: 做5个工作你可以这样称为copyFields:
/*5'*/copyFields<Source,{a?:"a"|"b"}>(makesrc(), {a: "b"});
但是对源类型的后续更改(例如删除&#34; b&#34;选项)现在将不再导致类型错误
有没有人知道如何使这项工作?
答案 0 :(得分:1)
我能想到的最好的解决方法是定义第二个接口(我称之为SourceParts
)与Source
完全相同,除了所有成员都是可选的。
function copyFields<T extends U, U>(target: T, source: U): T {
for (let id in source) {
target[id] = source[id];
}
return target;
}
function makesrc(): Source { return {b: 1, c: "a"}}
interface Source {
a?: "a"|"b",
b: number,
c: "a" | "b"
}
interface SourceParts {
a?: "a"|"b",
b?: number,
c?: "a" | "b"
}
/*1*/copyFields<Source, SourceParts>(makesrc(), {d: "d"}); //gives an error
/*2*/copyFields<Source, SourceParts>(makesrc(), {a: "d"}); //gives an error
/*3*/copyFields<Source, SourceParts>(makesrc(), {c: "d"}); //gives an error
//I don't want to specify all the source properties
/*4*/copyFields<Source, SourceParts>(makesrc(), {b: 2}); //will not give me an error
/*5*/copyFields<Source, SourceParts>(makesrc(), {a: "b"}); //will not give me an error
答案 1 :(得分:1)
interface Data {
a?: "a"|"b",
b: number,
c: "a" | "b"
}
function copyFields<T>(target: T, source: Readonly<Partial<T>>): T {
for (let id in source) {
target[id] = source[id];
}
return target;
}
function makesrc(): Data { return {b: 1, c: "a"}}
/*1*/copyFields(makesrc(), {d: "d"}); //gives an error
/*2*/copyFields(makesrc(), {a: "d"}); //gives an error
/*3*/copyFields(makesrc(), {c: "d"}); //gives an error
//I don't want to specify all the source properties
/*4*/copyFields(makesrc(), {b: 2}); //will not give me an error
/*5*/copyFields(makesrc(), {a: "b"}); //will not give me an error
答案 2 :(得分:0)
我之前已经完成了以下解决方案:
/**
* assign with known properties from target.
*
* @param target
* @param source
*/
public static safeAssignment(target: any, source: any) {
if (isNullOrUndefined(target) || isNullOrUndefined(source)) {
return;
}
for (const att of Object.keys(target)) {
target[att] = source.hasOwnProperty(att) ? source[att] : target[att];
}
}
我希望有人能有所帮助。 问候
答案 3 :(得分:-1)
我有这个功能:
/**
* Take every field of fields and put them override them in the complete object
* NOTE: this API is a bit reverse of extend because of the way generic constraints work in TypeScript
*/
const updateFields = <T>(fields: T) => <U extends T>(complete: U): U => {
let result = <U>{};
for (let id in complete) {
result[id] = complete[id];
}
for (let id in fields) {
result[id] = fields[id];
}
return result;
}
用法:
updateFields({a:456})({a:123,b:123}) // okay
updateFields({a:456})({b:123}) // Error
。
我之前在不同的背景下提到过这个功能:https://stackoverflow.com/a/32490644/390330
PS:一旦JavaScript进入第3阶段,事情会变得更好:https://github.com/Microsoft/TypeScript/issues/2103