У меня есть следующий код:
export type Primitive = boolean|string;
export type ObjectItem = {[k: string]: ObjectItem|Primitive};
export type DynamicItem<T extends ObjectItem|Primitive> = T extends ObjectItem ? ObjectItemInterface<T> : PrimitiveItemInterface<T>;
export interface PrimitiveItemInterface<T extends (ObjectItem|Primitive)> {
value(): T;
set(v: T): void;
}
export interface ObjectItemInterface<T extends ObjectItem> extends PrimitiveItemInterface<T> {
get<K extends keyof T>(k: K): DynamicItem<T[K]>;
}
const example: ObjectItemInterface<{
enabled: boolean
}> = null;
example.get('enabled').set(true); //Argument of type 'boolean' is not assignable to parameter of type 'never'.ts(2345)
Почему он думает, что есть тип never?
Я хочу, чтобы он видел логический тип.






это из-за дистрибутивного поведения boolean
решение состоит в том, чтобы остановить дистрибутивное поведение
export type DynamicItem<T extends ObjectItem | Primitive> = [T] extends [ObjectItem] ? ObjectItemInterface<T> : PrimitiveItemInterface<T>;
//========Дополнительно===========
но что странно, тип должен быть any не never
//=======Обновление=========
Я нашел объяснение, почему ОП так и не получил, объяснение, которое я цитирую из разногласий:
когда вы объявляете тип, предполагаемые типы параметров функции не переопределять, поэтому v может быть любым в литерале функции (потому что typescript отказывается делать вывод из типа объединения A), но когда вы вызовите a, typescript делает true & false = никогда и отказывается разрешать ты звонишь
a
Проблема в том, что вы используете boolean в качестве открытого типа параметра, из-за чего он используется как распределительный условный тип, поскольку boolean — это true|false. Вы можете увидеть в:
let r = example.get('enabled');
// ^? r: PrimitiveItemInterface<false> | PrimitiveItemInterface<true>
r.set(true);
Один стандартный трюк/решение (см., например, этот ответ) состоит в том, чтобы использовать кортежи: [T] extends [ObjectItem] вместо T extends ObjectItem, чтобы параметр не был голым:
export type DynamicItem<T extends ObjectItem|Primitive> = [T] extends [ObjectItem] ? ObjectItemInterface<T> : PrimitiveItemInterface<T>;
ссылка на игровую площадку - теперь видите, что r имеет тип PrimitiveItemInterface<boolean>, который вы изначально ожидали.