Это открытый вопрос в TypeScript (см. Microsoft / TypeScript # 33912) , что компилятор, как правило, не может проверить, соответствует ли возвращаемое значение конкретной функции условному типу, который зависит от пока еще неопределенный параметр типа generi c, например Ret<T>
внутри реализации a()
, где T
не разрешена. Это связано с тем, что TypeScript не может сузить параметры ввода с помощью анализа потока управления (см. Microsoft / TypeScript # 13995) , поэтому проверка id
с помощью оператора switch
/ case
может сузить тип id
, скажем, "bar"
, но он не сужает параметр типа T
до "bar"
, и, таким образом, он не может гарантировать, что Ret<"bar">
является приемлемым вывод.
Одна вещь, которую вы можете сделать, это принять, что компилятор не может проверить это для вас, и использовать утверждения типа или перегрузку , чтобы ослабить Реализация набирает достаточно, чтобы избежать ошибок. Это будет работать, но компилятор не гарантирует безопасность типов. Например, с перегрузкой:
function aOverload<T extends Ids>(id: T): Ret<T>;
function aOverload(id: Ids): Ret<Ids> {
switch (id) {
case "bar":
return { bar: 1 };
case "foo":
return { foo: "foo" };
case "bazz":
return { bazz: true };
}
}
теперь нет ошибок, и есть некоторая безопасность типов ... вы не можете вернуть совершенно неправильный тип, такой как {spazz: true}
, но вы можете поменять местами case
, и он не заметит:
function aBadOverload<T extends Ids>(id: T): Ret<T>;
function aBadOverload(id: Ids): Ret<Ids> {
switch (id) {
case "bazz":
return { bar: 1 };
case "bar":
return { foo: "foo" };
case "foo":
return { bazz: true };
}
}
Поэтому вы должны быть осторожны.
Еще одно решение этого конкретного случая - отказаться от условные типы в пользу индексации generi c, например:
interface RetMap {
foo: IFoo,
bar: IBar,
bazz: IBazz;
}
function aGood<K extends keyof RetMap>(id: K): RetMap[K] {
return {
bar: { bar: 1 },
foo: { foo: "foo" },
bazz: { bazz: true }
}[id];
}
const bar: IBar = aGood("bar");
const foo: IFoo = aGood("foo");
const bazz: IBazz = aGood("bazz");
Компилятор может проверить, что то, что мы здесь делаем, безопасно, потому что мы индексируем объект типа RetMap
с помощью клавиши id
типа K
. Да, и если вы недовольны тем, что эта версия предварительно вычисляет возвращаемые значения, которые она не будет использовать, вы можете изменить рефакторинг для использования getters , что также устраивает компилятор:
function aGood<K extends keyof RetMap>(id: K): RetMap[K] {
return {
get bar() { return { bar: 1 } },
get foo() { return { foo: "foo" } },
get bazz() { return { bazz: true } }
}[id];
}
Хорошо, надеюсь, это поможет вам продолжить; удачи!
Детская площадка ссылка на код