Объект TypeScript. путаница в назначении

#typescript

#typescript

Вопрос:

Когда я смотрю на подпись Object.assign , я вижу, что она использует типы пересечений, но поведение не то, что я ожидал.

Я хочу использовать Object.assign для объединения некоторых изменений со старым объектом для создания нового объекта (как это принято в JS при следовании неизменяемым шаблонам данных) и заставить TypeScript проверить правильность частей и результата, но, похоже, TypeScript допускает все без ошибок.

Например:

 interface Thing {
    name: string;
    age: number;
    fav: boolean;
}

let oldThing: Thing = {
    name: "Aaa",
    age: 123,
    fav: false
};

let newThing: Thing = Object.assign({}, oldThing, {
    name: "Bbb",
    age: "abc", // should be type error
    fake: "fakey" // should be unknown prop error
});
  

Вот он на игровой площадке.

Почему Object.assign разрешить это неправильное присвоение Thing ? Есть ли способ сделать эту работу близкой к тому, что я ожидал?

Комментарии:

1. Вы выполнили все требования, чтобы компилятор typescript был доволен, но помните, что в конце концов ваш код выполняется на javascript, а javascript просто не волнует.

2. Да, мне постоянно об этом напоминают. 🙂 Но мой вопрос здесь в том, почему он компилируется ? Кажется, что назначение Thing должно быть ошибкой компиляции для этих свойств. Возможно, я не понимаю, как средство проверки типов проверяет назначение.

3. Прошло почти 3 года, до сих пор нет прогресса Object.assign . Я немного смущен тем, почему так мало информации об этой проблеме. Я думаю, это должно быть описано в официальных документах.

Ответ №1:

Причина, по которой вы интуитивно ожидаете здесь ошибки, заключается в том, что тип пересечения, созданный для возвращаемого значения Object.assign , является абсурдным и не может быть занят никакими реальными значениями. Однако TypeScript не имеет способа проверить это. Возвращаемое отсюда значение Object.assign вводится следующим образом:

 {
    name: string,
    age: string amp; number,
    fav: boolean,
    fake: string
}
  

Конечно, нет фактического значения, которое было бы и тем, и другим string amp; number , но компилятор этого не знает. Он успешно создает этот абсурдный тип, затем, когда вы пытаетесь присвоить его Thing , проверяет, что тип каждого свойства в Thing удовлетворяется соответствующим свойством в возвращаемом значении. Поскольку это так (каждый из name age и fav присутствуют и удовлетворяют, по крайней мере, требуемому интерфейсу в Thing ), назначение выполняется успешно.

Если бы вы разрешили выводить тип newThing , а затем попытались присвоить что-либо age свойству, вы бы увидели проблему:

 let newThing = Object.assign({}, oldThing, {
    name: "Bbb",
    age: "abc",
    fake: "fakey"
});
newThing.age = 10;   // Compiler error because 10 is not a string
newThing.age = "10"; // Compiler error because "10" is not a number
  

Комментарии:

1. Спасибо, это забавно. Так что, я думаю Object.assign , на самом деле это не помогает обеспечить безопасность типов для подобных вещей. С нетерпением ждем, чтобы вместо этого TS поддерживал распространение объектов…

Ответ №2:

Вы можете заставить TypeScript проверять, используя утверждения типа: т.е. as Thing Или <Thing>

 interface Thing {
    name: string;
    age: number;
    fav?: boolean;  // You may need to mark this as optional depending on
                    // your requirement, otherwise TypeScript will 
                    // generate missing prop error
}

//...

let newThing: Thing = Object.assign({}, oldThing, {
    name: "Bbb",
    age: "abc",     // will cause type error
    fake: "fakey"   // howerver, no error for unknown prop
} as Thing);
  

Комментарии:

1. Это правильный ответ. Спасибо! Я не знал, что могу указать тип объекта с <Thing>{ ... } помощью синтаксиса.