简体   繁体   English

依赖泛型参数作为 function 的值参数的类型

[英]Type that depends on generic argument as value argument to function

I have a generic type Group that looks like this:我有一个看起来像这样的通用类型Group

// K -> Key
// I -> Input
type Group<K, I> = {
    key: K;
    func: (i: I) => void;
};

There is a fixed number of Group values which I declared in an object like this:我在 object 中声明了固定数量的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;

I then have 2 utility types to refer to all the possible group keys and all the possible group inputs:然后,我有 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

Finally, I have a function that receives both a group key and a group input:最后,我有一个 function 接收组键和组输入:

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?
    }
}

This function is generic over the type of the key that is passed in and unfortunately, TypeScript cannot "understand" that if key is "b" , then input is of type { y: number } .这个 function 对于传入的键的类型是通用的,不幸的是,TypeScript 无法“理解”如果key"b" ,那么input的类型是{ y: number } Why is this the case, what is TypeScript missing to be able to do this?为什么会出现这种情况,TypeScript 缺少什么才能做到这一点? I'd especially like to find a GitHub issue on this (so that I can subscribe to it), but I wasn't able to find one as this type of thing is particularly hard to search for.我特别想在这个问题上找到一个 GitHub 问题(以便我可以订阅它),但我无法找到一个,因为这种类型的东西特别难以搜索。

Full Playground URL 全游乐场 URL

Please consider this snippet:请考虑这个片段:

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

The input might be independent from the key . input可能独立于key There are no guarantees input.y must be a number when test is called with value 'b' as the first argument.不能保证input.y必须是一个数字,当test被调用时,值'b'作为第一个参数。 type TestType = GroupInput<"b"> uses literal type ( 'b' ) which allows Typescript to restrict 'a' | 'b' type TestType = GroupInput<"b">使用文字类型( 'b' ),它允许 Typescript 限制'a' | 'b' 'a' | 'b' to just 'b' . 'a' | 'b''b' The same applies for test('b', ...) , but passing key of type 'a' | 'b'这同样适用于test('b', ...) ,但传递类型为'a' | 'b' 'a' | 'b' allows to pass input of type GroupInput<'a' | 'b'> 'a' | 'b'允许传递GroupInput<'a' | 'b'>类型的输入GroupInput<'a' | 'b'> . GroupInput<'a' | 'b'>

One option would be to check if 'y' in input , but this still does not address the main issue of not allowing wrong arguments to test .一种选择是检查'y' in input ,但这仍然不能解决不允许错误的 arguments 进行test的主要问题。 In general case input as GroupInput<'b'> is unsafe and should be avoided at any cost.通常情况下input as GroupInput<'b'>是不安全的,应不惜一切代价避免。

A possible fix:一个可能的修复:

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

Playground 操场

Because the input parameter is independent from your key parameter.因为input参数独立于您的key参数。 input is not required to have y as a property although your key might be equal to 'b' .尽管您的键可能等于'b' ,但input不需要将y作为属性。 You would have to Typecast your input :您必须对您的input进行类型转换:

 function test<K extends GroupKey>(key: K, input: GroupInput<K>) {
    if (key === "b") {
        const typedInput = input as GroupInput<'b'>;
        (typedInput.y)
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM