gpt4 book ai didi

typescript - 在 Typescript 中,如何使用所述类型的文字类型属性从联合中选择类型?

转载 作者:行者123 更新时间:2023-12-04 04:26:57 26 4
gpt4 key购买 nike

我有一个 reducer react 。 Action 可以是 8 种类型之一,但为了简单起见,让我们假设只有 2 种类型

type Add = {
type: 'add';
id: string;
value: string;
}

type Remove = {
type: 'remove';
id: string;
}

type Action = Add | Remove;
我没有使用 switch case,而是使用了一个处理程序对象,其中每个处理程序都是一个处理特定操作的函数
const handlers = {
add: (state, action) => state,
remove: (state, action) => state,
default: (state, action) => state,
}

const reducer = (state, action) => {
const handler = handlers[action.type] || handlers.default;
return handler(state, action);
}
现在我想输入 handlers适本地反对​​。因此,处理函数应该接受与其在 handlers 中的键对应的类型的操作。目的。
type Handlers = {
[key in Action["type"]]: (state: State, action: Action) => State
// ↑this here should be the action which has type
// matching to it's key. So when the key is
// 'add', it should be of type Add, and so on.
}
我能想到的就是明确说明键和匹配的操作类型。有没有办法根据键的值从联合中“挑选”类型?

最佳答案

仅提取类型很简单。

type Add = {
type: 'add';
id: string;
value: string;
}

type Remove = {
type: 'remove';
id: string;
}

type Action = Add | Remove;

type addType = Extract<Action, {type: 'add'}>;
您可以创建一个映射类型来为联合的每个元素自动执行此操作。
type OfUnion<T extends {type: string}> = {
[P in T['type']]: Extract<T, {type: P}>
}
这是一种映射类型。它就像一个类型函数,它接受一个类型,转换它,然后返回那个新类型。所以 OfUnion期待形状像 T extends {type: string} 的东西.这意味着它会接受 {type: 'add'}或者像 type Action = Add | Remove 这样的联合类型.重要的是该联合类型的每个元素都有一个 type: string属性(property)。
当您有一个联合类型并且每个选项都有一个共同的属性时,您可以安全地访问该属性(在本例中为 type )。这被分发,所以 Action['type']给我们 'add' | 'remove' .映射类型正在创建一个具有键 [P in T['type']] 的对象。 ,又名 'add' | 'remove' ! add的类型属性应该是该操作的类型, Add .你可以用那个 Extract<> 来解决这个问题调用我。这实质上过滤了联合以仅包含匹配特定类型的元素。在这种情况下,过滤到仅匹配 {type: 'add'} 的内容.与此匹配的唯一选项是 {type: 'add', id: string, value: string}对象,所以这就是它的设置。
现在你有一个看起来像这样的对象:
{
add: {
type: 'add';
id: string;
value: string;
},
remove: {
type: 'remove';
id: string;
}
}
那太棒了!但是我们需要将其转换为处理程序。所以处理程序仍然会有 addremove键,但不是对象类型,它们将是采用对象类型并返回任何内容的函数。
type Handler<T> = {
[P in keyof T]: (variant: T[P]) => any
}
这里是 P 的例子将是 'add'T[P]就是 Add类型。所以现在处理程序应该接收一个 Add对象,并用它做一些事情。这正是我们想要的。
现在您可以编写一个带有自动完成功能的类型安全的匹配函数:
function match<
T extends {type: string},
H extends Handler<OfUnion<T>>,
> (
obj: T,
handler: H,
): ReturnType<H[keyof H]> {
return handler[obj.type as keyof H]?.(obj as any);
}
请注意,您需要在此处使用 extends 来推断 H 的特定类型。您需要完全指定的类型来获取每个分支的返回类型。
edited playground link match()函数需要接受一个对象,该对象将有一个联合类型,如 Action然后将它期望的处理程序对象限制为某个契约(Contract)(我们刚刚创建的处理程序类型)。最好将其作为函数而不是每种情况来执行,因为这样您就不需要不断重写类型定义!该功能将带来它们。最好在没有状态对象的情况下以不可知的方式执行此操作,因为您仍然可以在处理程序分支内使用状态对象。查找“闭包”以获取更多信息,但您也可以相信我, state对象将在范围内。但是现在,我们也可以使用 matchstate的地方不相关,就像在 .jsx 中一样。
const reducer = (state: State, action: Action) => match(action, {
add: ({id, value}) => state,
remove: ({id}) => state,
})
我希望这有帮助!相信我,使用这些类型比编写它们更容易。您需要高级 typescript 知识来编写它们,但任何初学者都可以使用它们。

您可能感兴趣 my library variant , which does this exact thing .事实上,这些例子只是 simplified versions of my library code .我的 match函数将自动限制您传入的处理程序对象,返回类型将是所有处理程序分支的联合。
欢迎你偷这个,或者,自己试试这个库。它将在同一方向上为您提供更多内容。

关于typescript - 在 Typescript 中,如何使用所述类型的文字类型属性从联合中选择类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64527150/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com