在使用类型进行函数编程时,通常可以将应用程序中的类型限制为提供的输入值。
但是我对如何使用TypeScript以及Map的键的可能值不知所措。
请考虑以下内容:
const match = <T, V, R>(p : (x : V) => T, ... c : Array< [T, (x : V) => R] >) : (x : V) => R => {
const cases = new Map(c);
return (x : any) => cases.get(p(x))(x);
}
更易理解的格式:
const match = <Row, Value, ReturnType>(p : (x : Value) => Row, ...c : Array< [Row, (x : Value) => ReturnType] >) : (x : Value) => ReturnType => {
const cases = new Map(c);
return x => cases.get(p(x))(x);
}
但是,这种表示法存在问题,因为它实际上没有按照计划执行:
match(x => Math.random() > 0.5 ? "foo" : "bar", ["baz", x => x])
在一个足够先进的打字系统中,这应该是一个错误,但是在TypeScript中,它没有检测到“ foo”或“ bar”值不是任何预期值,并且实际上会导致代码扔。
更深入的示例:
const validDouble = match( x => x * 2,
[4, x => "four"],
[6, x => "six"]
)
validDouble(4) // TS ERROR: 8 is not a valid value for Type ROW (4 | 6)
validDouble(2) // "four", no TS Error
我知道我不能将rest参数强制为数组的强制值(但是我可以将其包装在强制数组中,并且需要开发人员提供额外的字符),但是我很好奇是否有一种方法可以强制执行该行中的值与谓词函数中的输出值匹配吗?
编辑:是的,我知道这引发了不匹配的输入。这种逻辑很容易插入条件语句或尝试捕获并将其转换为matchMaybe,但是此示例已简化,因为它对TypeScript中可用的键入系统没有影响或暗示
答案 0 :(得分:1)
好的,这是固定功能(可能需要一些整理和可能的改进)。
const match = <
Row extends string | number,
Value,
ReturnType,
V extends (x: Value) => ReturnType,
C extends [...[Row, V][]],
>
(
predicate: (x: Value) => Row,
...c: C & [...[Row, V][]]
): (x: Value) => ReturnType => {
const cases = new Map<Row, V>(c);
return x =>
// put a runtime check below instead of asserting
// non-undefined value with `!`
cases.get(predicate(x))!(x)
}
match(x => 'baz', ["baz", (x: any) => x]) // ok
match(x => 'qwe', ["baz", (x: any) => x]) // err
match(x => 'qwe', ["baz", (x: any) => x], ["qwe", (x: any) => x]) //ok
第一个问题是rest参数应该是一个元组。第二个问题是正确放置类型参数,以便所有内容都能正确推断。
P.S。打字稿中的复杂类型很难。我觉得缺乏有关类型参数的文档,因此我总是对它们不自信(我认为其他人也是如此)。