Тип, который зависит от общего аргумента в качестве аргумента значения для функции

#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 по этому поводу (чтобы я мог подписаться на него), но я не смог его найти, так как подобные вещи особенно трудно найти.

Полный URL-адрес игровой площадки

Ответ №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 принадлежит тому же ключу.