2016-08-18 2 views
5

Мы ищем безопасный способ использования Object.assign. Однако мы не можем заставить его работать.Безопасная печать объекта Object.assign

Чтобы показать нашу проблему, я буду использовать метод copyFields из Generics документации

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 

Мы попытались решить эту проблему с явным образом предоставляя типы вызовам копий , но мы не можем найти вызов, который заставит все примеры работать.

Например: сделать 5 работу, которую вы могли бы назвать copyFields так:

/*5'*/copyFields<Source,{a?:"a"|"b"}>(makesrc(), {a: "b"}); 

но последующие изменения типа источника (например, удаление вариант «Б») не будет теперь уже не приводит в тип ошибки

Кто-нибудь знает, как это сделать?

+1

Посмотрите на '' & оператора типа. –

+0

BTW мы (Рене мой коллега) обнаружил, что поток не поддерживает этот случай использования https://tryflow.org/?code=ZGVjbGFyZSBpbnRlcmZhY2UgU291cmNlIHsKICAgIGE/OiAiYSIgfCAiYiIsCiAgICBiOiBudW1iZXIsCiAgICBjOiAiYSIgfCAiYiIKfQpmdW5jdGlvbiBtYWtlc3JjKCkgOiBTb3VyY2UgeyByZXR1cm4ge2I6IDEsIGM6ICJhIn19CgovKjEqL09iamVjdC5hc3NpZ24obWFrZXNyYygpLCB7ZDogImQifSk7Ci8qMiovT2JqZWN0LmFzc2lnbihtYWtlc3JjKCksIHthOiAiZCJ9KTsKLyozKi9PYmplY3QuYXNzaWduKG1ha2VzcmMoKSwge2M6ICJkIn0pOwoKLyo0Ki9PYmplY3QuYXNzaWduKG1ha2VzcmMoKSwge2I6IDJ9KTsKLyo1Ki9PYmplY3QuYXNzaWduKG1ha2VzcmMoKSwge2E6ICJiIn0pOw== – Jauco

+0

@torazaburo, что не будет делать необходимые примеры не в состоянии – Jauco

ответ

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 
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.

+0

Это решение действительно имело место для нас, однако для нашего случая использования потребовалось бы много двойных шаблонов. – renevanderark

+0

Да, это, по крайней мере, объединяет два типаспециалистов, поэтому есть шанс, что вы их обновите. – Jauco

-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

+0

Хорошее решение, однако, наше желание (как упоминалось в комментарии): «Я не хочу указывать все исходные свойства» – renevanderark

+1

@renevanderark не нужно «обновлять поля» ({a: 456}) ({a : 123, b: 123}) 'only' a' указан (у источника есть 'a' и' b') – basarat

+1

Прошу прощения. Эта функция действительно такая же, как наша, но с карри. Я попробовал это на игровой площадке (ссылка слишком длинная, чтобы вставить здесь), и она терпит неудачу на тех же тестах (3 и 5) – Jauco

Смежные вопросы