#typescript
#машинописный текст
Вопрос:
У меня есть общий тип Group
, который выглядит следующим образом:
// K -> Key
// I -> Input
type Group<K, I> = {
key: K;
func: (i: I) => void;
};
Существует фиксированное количество Group
значений, которые я объявил в объекте, подобном этому:
const GROUPS = {
"a": {
func: (i: {x: number}) => { console.log(i); },
key: "a",
},
"b": {
func: (i: { y: number }) => { console.log(i) },
key: "b"
}
} as const;
Затем у меня есть 2 типа утилит для ссылки на все возможные групповые ключи и все возможные групповые входы:
type GroupKey = keyof typeof GROUPS;
type GroupInput<K extends GroupKey> = Parameters<typeof GROUPS[K]["func"]>[0];
// GroupValue test:
type TestType = GroupInput<"b">; // { y: number}, this works
Наконец, у меня есть функция, которая получает как групповой ключ, так и групповой ввод:
function test<K extends GroupKey>(key: K, input: GroupInput<K>) {
if (key === "b") {
(input.y); // Why doesn't TypeScript understand that `input.y` must be `number` here?
}
}
Эта функция является общей для типа передаваемого ключа, и, к сожалению, TypeScript не может «понять», что если key
есть "b"
, то input
имеет тип { y: number }
. Почему это так, чего не хватает TypeScript, чтобы иметь возможность это сделать? Мне бы особенно хотелось найти выпуск GitHub по этому поводу (чтобы я мог подписаться на него), но я не смог его найти, так как подобные вещи особенно трудно найти.
Ответ №1:
Пожалуйста, рассмотрите этот фрагмент:
const key = 'a' as GroupKey
const input = { y: 1 } // optionally cast as GroupInput<'b'> or as GroupInput<GroupKey>
test(key, input) // compiles, but not intended
input
Может быть независимым от key
. Нет никаких гарантий input.y
, что при test
вызове со значением 'b'
в качестве первого аргумента должно быть число.
type TestType = GroupInput<"b">
использует литеральный тип ( 'b'
), который позволяет Typescript ограничиваться 'a' | 'b'
только 'b'
. То же самое относится к test('b', ...)
, но передача ключа типа 'a' | 'b'
позволяет передавать ввод типа GroupInput<'a' | 'b'>
.
Одним из вариантов было бы проверить, если 'y' in input
, но это все еще не решает основную проблему недопущения неправильных аргументов test
. В общем случае input as GroupInput<'b'>
это небезопасно, и его следует избегать любой ценой.
Возможное исправление:
type Params = { [K in GroupKey]: [key: K, input: GroupInput<K>] } // key: and input: are used for auto-completion instead of generic arg_0, arg_1
function test2(...args: Params[GroupKey]) {
// const [key, input] = args // will not work
if (args[0] === "b") {
const input = args[1];
input.y; // number
}
}
test2('b', { y: 1 }) // ok
test2('b', { x: 1 }) // error
test2(key, input) // error
Ответ №2:
Потому что этот input
параметр не зависит от вашего key
параметра. input
не обязательно иметь y
в качестве свойства, хотя ваш ключ может быть равен 'b'
. Вам пришлось бы набирать свой input
:
function test<K extends GroupKey>(key: K, input: GroupInput<K>) {
if (key === "b") {
const typedInput = input as GroupInput<'b'>;
(typedInput.y)
}
}
Комментарии:
1. я не видел вашего ответа. Я удалил свой ответ.
2. Приведение типов не является отличным решением, это опасно, поэтому я бы хотел его избежать.
3. К сожалению, обойти это невозможно, поскольку вы не можете гарантировать, что ваш GroupInput принадлежит тому же ключу.