如何在TS中输入这个接受类型对象的函数?



我有一个函数,它接受一个键的对象,并且每个值都有一个类型,使得对于每个,其中一个字段的类型决定了另一个字段的类型。代码:

// We have this Alpha type and echo function...
type NoInfer<T> = [T][T extends unknown ? 0 : never]
interface Alpha<Foo extends string> {
foo: Foo
bar: `Depends on ${NoInfer<Foo>}`
}
declare const echo: <T extends string>(x: Alpha<T>) => void
echo({ foo: 'beta', bar: 'Depends on beta'})
// @ts-expect-error Trailing 2 is wrong
echo({ foo: 'beta', bar: 'Depends on beta 2'})
// Now we want a function (bravo) that takes a keyed index of Alphas...
declare const bravo: <T extends { [k: string]: Alpha<string> }>(xs: T) => void
bravo({
one:  { foo: `1`,  bar: `Depends on 1` },
// @ts-expect-error 1 !== 1x           <-- fails
oneX: { foo: `1x`, bar: `Depends on 1` },
two:  { foo: `2`,  bar: `Depends on 2` },
// @ts-expect-error 2 !== 2x           <-- fails
twoX: { foo: `2x`, bar: `Depends on 2` },
})
// how could this work?

操场上联系

从"失败"中可以看出我可以让Alpha开始工作,但在更复杂的Alpha对象中我失败了。你能帮我弄明白吗?谢谢!

您可以这样写,以便T是一个对象类型,其属性是您作为类型参数传递给Alphastring,然后使xs成为T的映射类型,如下所示:

declare const bravo: <T extends { [K in keyof T]: string }>(
xs: { [K in keyof T]: Alpha<T[K]> }
) => void

请注意,递归约束{ [K in keyof T]: string }用于保证T的每个属性都是string,而不使用索引签名{ [k: string]: string },这将拒绝没有索引签名的接口类型(参见microsoft/TypeScript#15300和如何约束TypeScript接口只有字符串属性值?

无论如何,因为xs的类型是一个同态映射类型(参见什么是"同态映射类型")。mean?),那么编译器可以在调用函数时从中推断出T(这曾经有文档记录,但新的手册似乎没有提到🤷‍♂️)。让我们测试一下:
bravo({
one: { foo: `1`, bar: `Depends on 1` },  // okay
oneX: { foo: `1x`, bar: `Depends on 1` }, // error
// --------------> ~~~
// Type '"Depends on 1"' is not assignable to type '"Depends on 1x"'
two: { foo: `2`, bar: `Depends on 2` }, // okay
twoX: { foo: `2x`, bar: `Depends on 2` }, // error
// --------------> ~~~
// Type '"Depends on 2"' is not assignable to type '"Depends on 2x"'
})

看起来不错。如果你将鼠标悬停在启用智能感知的IDE中的函数调用上,你会看到快速信息

/* const bravo: <{
one: "1";
oneX: "1x";
two: "2";
twoX: "2x";
}>(xs: {
one: Alpha<"1">;
oneX: Alpha<"1x">;
two: Alpha<"2">;
twoX: Alpha<"2x">;
}) => void */

显示T被推断为{one: "1", oneX: "1x", two: "2", twoX: "2x"},因此xs的类型是针对{one: Alpha<"1">, oneX: Alpha<"1x">, two: Alpha<"2">, twoX: Alpha<"2x">}进行检查的,onetwo属性成功,但oneXtwoX属性失败,给你你想要的错误。

Playground链接到代码

相关内容

  • 没有找到相关文章

最新更新