安全输入Object.assign

时间:2016-08-18 08:20:24

标签: typescript

我们正在寻找使用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;选项)现在将不再导致类型错误

有没有人知道如何使这项工作?

4 个答案:

答案 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 

此处位于Typescript Playground

答案 1 :(得分:1)

打字稿2.1.4救援!

Playground link

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