简体   繁体   English

TypeScript中的函数定义中的嵌套泛型

[英]Nested Generics in function definition in TypeScript

I'm trying to make type-safe handlers for redux actions relying on action type. 我正在尝试为依赖于操作类型的redux操作创建类型安全的处理程序。 For example, any action could be described as: 例如,任何动作都可以描述为:

type ActionType<T extends string> = {
  type: T
};

For a specific action one can have: 对于特定的动作,可以有:

type MyAction = ActionType<'hey' | 'there'>;

Now, I'd like to restrict a handler function to allow only 'hey' or 'there' as a type. 现在,我想将处理程序函数限制为仅允许“ hey”或“ there”作为类型。 Finally I expect something like this: 最后,我期望这样的事情:

handleAction<MyAction>('hey');

where the definition of the handler function could be: 处理程序函数的定义可以是:

function handleAction<A extends ActionType<T>>(type: T){
...
}

But I have a typescript compiler error: 但是我有一个打字稿编译器错误:


TS2304: Cannot find name 'T'. TS2304:找不到名称“ T”。


So, I have to modify this handler function definition in this way: 因此,我必须以这种方式修改此处理程序函数定义:

function handleAction<A extends ActionType<T>, T extends string>(type: T){
...
}

It works, but looks really ugly: 它可以工作,但是看起来真的很丑:

handleAction<MyAction, 'hey' | 'there'>('hey');

TS Playground TS游乐场

What's a better way to handle this? 有什么更好的方法来解决这个问题?

Edit 编辑

You can use a type query to get the type of type : 您可以使用类型查询来获取类型的type

function handleAction<A extends ActionType<string>>(type: A ['type']) {
}

handleAction<MyAction>('hey');

Or you can use conditional types in 2.8 to extract the generic parameter from ActionType (2.8 is unreleased at the time of writing, but will be released in March 2018, you can get it via npm install -g typescript@next ) 或者,您可以在2.8中使用条件类型从ActionType提取通用参数(在撰写本文时尚未发布2.8,但将于2018年3月发布,您可以通过npm install -g typescript@next来获取它)

type ActionType<T extends string> = {
  type: T
};

type MyAction = ActionType<'hey' | 'there'>;


function handleAction<A extends ActionType<string>>(type: A extends ActionType<infer U> ? U : never) {
}

handleAction<MyAction>('hey');

This solution is based on the inference behavior of conditional type . 该解决方案基于条件类型推断行为 ActionType<infer U> basically says: If a extends ActionType of some other type U give me the type U . ActionType<infer U>基本上说:如果扩展了其他类型U ActionType ,则给我类型U So U will be whatever string literal type was passed to ActionType in our case. 因此,在本例中, U将是传递给ActionType任何字符串文字类型。 Then we use U on the true branch of the conditional type and this becomes the final type of out conditional type. 然后,我们在条件类型的真实分支上使用U ,这成为out条件类型的最终类型。 In this case we don't care about the false branch so we use never. 在这种情况下,我们不在乎虚假分支,因此我们从不使用。

There are two approaches: 有两种方法:

  1. Use the string union. 使用字符串并集。

I like this one because its simpler (for the problem you described): 我喜欢这个,因为它比较简单(针对您描述的问题):

type FileAction = 'save' | 'read' | 'copy';
function doAction (a) {
}
declare function doAction<A extends string> (action: A): void;

doAction<FileAction>('save'); // good, as desired
doAction<FileAction>('xxx'); // error, as expected
  1. Get .type via bracket notation. 通过括号符号获取.type

This is one looks more hacky but it's a valid syntax .. and it does not require you to change your object shape. 这看起来更骇人听闻,但它是一种有效的语法..它不需要您更改对象的形状。

type FileFile <T extends string> = {
  type: T;
}
type WordFile = FileFile<'why'|'you'|'no'|'work'>;

function doAction2 (a) {
}
declare function doAction2<F extends FileFile<string>> (action: F["type"]): void;

doAction2<WordFile>('no'); // good, as desired 
doAction2<WordFile>('xxx'); // error, as expected

This cleans things up some, but I'm not sure if it's what you're hoping for: 这可以清理一些东西,但是我不确定这是否是您想要的:

type ActionType<T extends string> = {
  type: T
};

type MyWord = 'hey' | 'there';
type MyAction = ActionType<MyWord>;

function handleAction<A extends ActionType<T>, T extends string>(type: T){
}

handleAction<MyAction, MyWord>('hey');

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

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