Почему в этом случае TypeScript выводит неправильный тип?

#javascript #typescript #types

Вопрос:

У меня есть следующий код , и машинопись выводит тип переменной d как a string , но на самом деле это должно было быть a string | number . Я понимаю, что это происходит из-за функции машинописи, называемой сужением типа из-за назначений. Но это может стать довольно небезопасным (как подразумевается в следующем коде). Как отказаться от такого поведения?

 interface Z {
    name: string | number;
}

const f = (z: Z) => {
    z.name = 200;
};

let z: Z = {
    name: 2,
};

z.name = '2';

f(z);

const d = z.name;

 

Редактировать: Похоже, что один из способов обойти это-иметь какой-то линтер, делающий параметр немодифицируемым (но это очень ограничивает).

Ответ №1:

Я не уверен, что такое поведение «настраивается».

Ваша f функция изменяет тип свойства z своего аргумента, и я предполагаю , что компилятор не проверяет это так глубоко.

Если вы хотите, чтобы ввод был правильным, вам придется сообщить TypeScript, что вы что-то делаете с переменной.

Это сработало бы:

 interface Z { name: string | number; }

const f = (z: Z) => {
    z.name = 200;
    return z;
};

let z: Z = { name: 2 };

z.name = '2';

z = f(z);

const d = z.name;
 

замена z возвращенным значением заставляет TS «сохранять» изменение типа, которое происходит в f(z) .

Или это:

 interface Zbase { name: any }
interface Z       extends Zbase { name: string | number; }
interface Znumber extends Zbase { name: number; }
interface Zstring extends Zbase { name: string; }

const f = (z: Z) => { z.name = 200; };
let z: Z = { name: 2 };

z.name = '2';

f(z);

const d = (z as Znumber).name;
 

Сейчас все это не очень красиво, но я обычно воспринимаю это как признак того, что я делаю что-то не так, когда пишу TS:

Почему а может name быть а number или а string ?
Как еще я могу подойти <requirement> , чтобы правильно ввести свои свойства?