这可以是强类型的吗?
Can this be strongly typed?
我正在尝试在 Typescript 中实现一种伪模式匹配,使用它们对可区分联合的支持,利用匹配函数以及表示匹配表达式分支的对象。
这是我希望能够使用的场景:
type Shape =
| { kind: 'Circle', radius: number }
| { kind: 'Rectangle', height: number, width: number }
function printShape(s: Shape) {
return document.write(
match(s, {
'Circle': c => `Circle(${c.radius})`,
'Rectangle': r => `Rectangle(${r.width} x ${r.height})`
}));
}
我目前对匹配函数定义的尝试如下所示:
function match<T extends { kind: V }, V extends string, R>(
x: T, branches: { [P in T['kind']]: (arg: T & { 'kind': P }) => R }) {
return branches[x.kind](x);
}
这很接近,但不幸的是不太行得通;虽然我已经成功地让编译器抱怨给定匹配的完整性,但分支函数的参数类型不正确:参数 c
和 r
的类型为 any
.
我可以接受 kind
作为硬编码鉴别器,但我通常不了解 Typescript 如何从通用类型联合中过滤可能性。例如,我将练习提炼为尝试编写以下内容:
type Where<T, K extends keyof T, V extends T[K]> = ???
我的类型约束是正确的,因为我在编写时从编译器得到了关于我的类型和文字的正确验证:
type Circle = Where<Shape, 'kind', 'Circle'>
但我不明白我可以在该类型表达式的右侧写什么 return:
{ kind: 'Circle', radius: number }
要从联合中提取特定类型,您可以使用 Extract
条件类型。这将提取作为第二个参数子类的联合的所有成员。所以你的 where 类型看起来像这样:
type Where<T, K extends keyof T, V extends T[K]> = Extract<T, Record<K, V>>
type C = Where<Shape, 'kind', 'Circle'> //{ kind: 'Circle', radius: number }
如果您使用一个 returns 函数的函数,您可以获得该函数的完整类型。第一次调用设置 T
,第二次调用可以使用类型信息来完整键入参数:
type Shape =
| { kind: 'Circle', radius: number }
| { kind: 'Rectangle', height: number, width: number }
function printShape(s: Shape) {
var r = match(s)({
'Circle': c => `Circle(${c.radius})`,
'Rectangle': r => `Rectangle(${r.width} x ${r.height})`
}) // r is string
return document.write(r);
}
function match<T extends { kind: V }, V extends string>(x: T) {
return function <R>(branches: { [P in T['kind']]: (arg: Extract<T, { 'kind': P }>) => R }) {
return branches[x.kind](x as any);
}
}
我正在尝试在 Typescript 中实现一种伪模式匹配,使用它们对可区分联合的支持,利用匹配函数以及表示匹配表达式分支的对象。
这是我希望能够使用的场景:
type Shape =
| { kind: 'Circle', radius: number }
| { kind: 'Rectangle', height: number, width: number }
function printShape(s: Shape) {
return document.write(
match(s, {
'Circle': c => `Circle(${c.radius})`,
'Rectangle': r => `Rectangle(${r.width} x ${r.height})`
}));
}
我目前对匹配函数定义的尝试如下所示:
function match<T extends { kind: V }, V extends string, R>(
x: T, branches: { [P in T['kind']]: (arg: T & { 'kind': P }) => R }) {
return branches[x.kind](x);
}
这很接近,但不幸的是不太行得通;虽然我已经成功地让编译器抱怨给定匹配的完整性,但分支函数的参数类型不正确:参数 c
和 r
的类型为 any
.
我可以接受 kind
作为硬编码鉴别器,但我通常不了解 Typescript 如何从通用类型联合中过滤可能性。例如,我将练习提炼为尝试编写以下内容:
type Where<T, K extends keyof T, V extends T[K]> = ???
我的类型约束是正确的,因为我在编写时从编译器得到了关于我的类型和文字的正确验证:
type Circle = Where<Shape, 'kind', 'Circle'>
但我不明白我可以在该类型表达式的右侧写什么 return:
{ kind: 'Circle', radius: number }
要从联合中提取特定类型,您可以使用 Extract
条件类型。这将提取作为第二个参数子类的联合的所有成员。所以你的 where 类型看起来像这样:
type Where<T, K extends keyof T, V extends T[K]> = Extract<T, Record<K, V>>
type C = Where<Shape, 'kind', 'Circle'> //{ kind: 'Circle', radius: number }
如果您使用一个 returns 函数的函数,您可以获得该函数的完整类型。第一次调用设置 T
,第二次调用可以使用类型信息来完整键入参数:
type Shape =
| { kind: 'Circle', radius: number }
| { kind: 'Rectangle', height: number, width: number }
function printShape(s: Shape) {
var r = match(s)({
'Circle': c => `Circle(${c.radius})`,
'Rectangle': r => `Rectangle(${r.width} x ${r.height})`
}) // r is string
return document.write(r);
}
function match<T extends { kind: V }, V extends string>(x: T) {
return function <R>(branches: { [P in T['kind']]: (arg: Extract<T, { 'kind': P }>) => R }) {
return branches[x.kind](x as any);
}
}