gpt4 book ai didi

typescript - 在 TypeScript 中自引用映射类型

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

我正在为 Typescript 中的一种小语言建模 AST。我正在尝试转换这种类型:

type Lang<T> = {
Literal: { value: string | number | boolean },
BinOp: { op: string, lhs: T, rhs: T },
UnOp: { op: string, arg: T },
// ...more fields
};

进入这个:

type ASTNode =
{ type: 'Literal', value: string | number | boolean } |
{ type: 'BinOp', op: string, lhs: ASTNode, rhs: ASTNode } |
{ type: 'UnOp', op: string, arg: ASTNode }
// ... more types
;

我认为解决方案是这样的:

type ASTNodeAux<T> = {
[K in keyof Lang<T>]: { type: K } & Lang<T>[K]
};
type ASTNode = ASTNodeAux<ASTNode>[keyof ASTNodeAux<ASTNode>];

但这不被 TypeScript 接受,因为 ASTNode引用自己。从我看到的解决方法通常是使用接口(interface)来代替,但我不知道如何与映射类型一起做到这一点。有什么办法可以做到这一点?

对于额外的上下文,我试图避免多次编写类型类型的需要,同时为 AST 节点和 fold 的参数提供准确的类型签名。 , forEach和其他组合器( playground link)。我能够在 Flow ( playground link) 中实现这一点(以一种不完美的方式)。

最佳答案

更新:经过你的操场后,你会更清楚为什么要一个 Lang<T>以这种方式指定。这是playground上的一些代码使用您自己示例的子集,其中 render , runfold似乎使用以下定义成功键入:

type Replace<T, B> = { [P in keyof T]: T[P] extends SSNode ? B : T[P] }
type ASTNodeMatch <K, T> = Omit<Extract<Replace<SSNode, T>, { type: K }>, 'type'>
type SSAction<T, U> = { [K in SSNode['type']]: (x: ASTNodeMatch<K, T>) => U }

我和 André Restivo在过去的 4 个小时里一直在研究这个 :) 我们提供两个答案,具体取决于您想走的方向。但是,我们认为您的 Lang<T>格式不正确。 T 到底是什么应该是?也许是终端值(value)?如果是这种情况,那么 lhsrhs应该指向 Lang<T>Literal应该是 T 类型而不是 string | number | boolean .另一件事是您的 Lang属性都应该是可选的(否则 Lang 的任何实例都会强制它提供 LiteralUnOpBinOp 等等......)我们相信您会适本地修复代码以匹配您的语言语义...

附言您可以摆脱许多“中间”类型,但我们相信这可以很容易地遵循基本原理。

从描述的联合类型转换为 Lang :
namespace OneWay {
/* type Lang<T> = {
Literal?: { value: string | number | boolean },
BinOp?: { op: string, lhs: T, rhs: T },
UnOp?: { op: string, arg: T },
} */

type ASTNode =
{ type: 'Literal', value: string | number | boolean } |
{ type: 'BinOp', op: string, lhs: ASTNode, rhs: ASTNode } |
{ type: 'UnOp', op: string, arg: ASTNode }

type Replace<T, A, B> = {
[P in keyof T]: T[P] extends A ? B : T[P]
}

type Lang<T extends ASTNode> = {
[K in T['type']]?: ASTNodeMatch<K>
}

type ASTNodeMatchReplaced<T> = Replace<T, ASTNode, Lang<ASTNode>>
type ASTNodeMatch<T> = Omit<Extract<ASTNodeMatchReplaced<ASTNode>, { type: T }>, 'type'>

const node: ASTNode = { type: 'Literal', value: "hello" }
const node2: Lang<ASTNode> = { Literal: { value: "string" } }
const node3: Lang<ASTNode> = { BinOp: { op: "+", lhs: node2, rhs: node2 } }
}

来自 Lang到一个描述的联合类型:
namespace OrAnother {
/* type ASTNode =
{ type: 'Literal', value: string | number | boolean } |
{ type: 'BinOp', op: string, lhs: ASTNode, rhs: ASTNode } |
{ type: 'UnOp', op: string, arg: ASTNode }
*/

type Lang = {
Literal?: { value: string | number | boolean },
BinOp?: { op: string, lhs: Lang, rhs: Lang },
UnOp?: { op: string, arg: Lang },
}

type Replace<T, A> = {
[P in keyof T]: T[P] extends A ? ASTNode : T[P]
}

type Pairs<T> = { [TKey in keyof T]: { type: TKey } & Replace<T[TKey], T> }
type ASTNode = Pairs<Lang>[keyof Lang]

const a1: ASTNode = { type: 'Literal', value: "dsda" }
const a2: ASTNode = { type: 'BinOp', op: "+", lhs: a1, rhs: a1 }

// These produce Type Errors (as expected)
const a4: ASTNode = { type: 'BinOp', op: "+", lhs: 3, rhs: a1 }
const a5: ASTNode = { type: 'Literal', op: "-" }
}

关于typescript - 在 TypeScript 中自引用映射类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61214426/

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