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

问题描述 投票:0回答:1

我有一个接受键对象的函数,每个值都有一个类型,这样对于每个它的一个字段的类型决定另一个字段的类型。代码:

// 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 对象中我失败了。你能帮我解决这个问题吗?谢谢!

typescript types type-inference
1个回答
1
投票

您可以这样写,这样

T
是一个对象类型,其属性是您作为类型参数传递给
string
Alpha
s,然后使
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
而无需使用 index 签名
{ [k: string]: string }
将拒绝没有索引签名的 interface 类型(参见 microsoft /TypeScript#15300How to constrain a TypeScript interface to have only string property values? for more info).

总之,因为

xs
的类型是homomorphic映射类型(参见What does "homomorphic mapped type" mean?),那么当你调用函数时,编译器可以从中推断出
T
(这使用被记录但新手册似乎没有提到它🤷u200d♂️)。让我们测试一下:

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"'
})

看起来不错。如果您在支持 IntelliSense 的 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">}
的类型,这对于
one
two
属性成功但对于
oneX
twoX 
属性,给你你想要的错误。

游乐场代码链接

© www.soinside.com 2019 - 2024. All rights reserved.