这个采用类型对象的函数如何在 TS 中键入?

2024-04-10

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

// 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?

游乐场链接 https://www.typescriptlang.org/play?#code/PTAEHUFNQCwQwG7QC4wJYGdQEEA2AHeUZAT32jgDsATUSAYxgHtQAzAV0vuTScoDpBAKCGlyoAHJMAkpVaQATgB4AKgD5QAXlABtFQF09dAB7JINLJwDWlJgHdKoAPygADKABcoSpCQL9ImiUZgqscPTQeIRwSgBiTCyQpubUWBjICkEA5hoA3kKgbAle8UwFoABGcApeAAYAIpDkFqB8oAAkuVKy8sqlagC+tUIDItQMuNXQ9HzpdIxMXqomZi3pmZQ5ABTGXlHwqmoAlFoaCExo1CIMzFu5RYugAOQVkMhwTwA0ldVeT43NVKtRyvd5PAZHEQgUAAAWQGAAtElyNwkQoFEwFKAVAo4GhcNlQAAmUCYUB2DGbIQ3Jh3B5-UEfb5VGrPAEpLBtRnE8GQoTQqR2cnQOxUZCgOBsTjcXiOLYVXHnE6oODi95WSBYSUakiQWhBcbGVqsHAEeAYQT8MYTKagGaUOYKxCPZZJVZA+46KxedbZfR7M0xX2bDQDNQ7DBeFQnTRnC5XIRO853cp8SBeUD3VjFUC1ACMtW+P1ZDSaHOBoALoAGn3K0LhiORDGQaIxWLzoAAhJptHnjKmfAANLxZnP54yF4t1dktNpVmvlZB2R6Z+m5omTqe5mdAtob6u1wr1+FI4woluKNvErs94lG0CL5fD1fZx61IkT5m-bdl2eOfcLhCUJgMwQozOwuC0KgZLLgoVhOEAA

正如您从“失败”评论中看到的那样,我最初可以使 Alpha 工作,但在更复杂的 Alpha 对象中我失败了。你能帮我解决这个问题吗?谢谢!


你可以这样写T是一个对象类型,其属性是string您作为类型参数传递给Alpha,然后使xs a 映射类型 https://www.typescriptlang.org/docs/handbook/2/mapped-types.html over T, 像这样:

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

请注意,递归约束 https://www.typescriptlang.org/docs/handbook/2/generics.html#generic-constraints { [K in keyof T]: string }用于保证T is string不使用索引签名 https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures { [k: string]: string }这会拒绝界面 https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#interfaces没有索引签名的类型(参见微软/TypeScript#15300 https://github.com/microsoft/TypeScript/issues/15300 and 如何限制 TypeScript 接口仅具有字符串属性值? https://stackoverflow.com/q/73954300/2887218了解更多信息)。

无论如何,因为类型xs is a 同态映射类型(参见“同态映射类型”是什么意思? https://stackoverflow.com/q/59790508/2887218),那么编译器可以推断T当你调用该函数时(这曾经是记录在案 https://www.typescriptlang.org/docs/handbook/advanced-types.html#inference-from-mapped-types但新的手册好像没有提到????‍♂️)。让我们测试一下:

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 and two属性但失败了oneX and twoX属性,为您提供您想要的错误。

Playground 代码链接 https://www.typescriptlang.org/play?#code/C4TwDgpgBAcg9gSQHYDMICcA8AVAfFAXigG1sBdUqCAD2AiQBMBnKAVyQGsk4B3JKAPxQADFABcUJBABuGMgCh5ASyR10KAIYBjaAEEANmAAWGzADE4cKrXrMoTYOhUBzfAG95UKCksSLcTygAIw10CQADABEISEYWOH4AEjd4ZDQsf1wAX3D5LMUGCC19UOgtBIcqLSM4CRxrOjj7RxdcAApqCQNjUzwASkJ8aTglBkUimra3b18oAHIgiGANOYAaYNCJOejYuwTgpZWoLL75Cbgpmdr5xeW1jbD5ndt4-luNKAAmOePT+ULiqUoOUkJUgugNMM6tgGi8oNNiABpKAqKAcCAgOAoKDkCQOJxIZzHdqBahMCQI5Go9GY7G4qDdEw4JFkfD5AYEIYjMbycGQi4eLwJCAUq4RACM4XWIUeURicP2kuO6ygAHpVVA4BwNCBAsKABqinzXcLi6hSh4RZ5NRXhZVqjUYdBwdCBdVQAC0Xu9Pp9+AAfoG3RrsOBoHMAETWvb8cURn5KFjcYBQDRMJhKZxIDRBfTQYBWUCQeZR+U22PUeOBYA8a7TY0RT4WmVWssxr52rLrd1anXV2uG+FiqDhT7m6WbEfR14d+3up0u4Oe30rr0BoNed2h4uR6ea-ifeMopNwFNpjNZnN5qAFm9hkt7-ZjqsnRSqgBUwIqKb5UKgmEFLx9xFKAIzjABuQIhSkQcwMrSCgJvWsJAjQ8EKAms4Fg59IKydoyQpKDgK6QwmTAiNcHQ6CIEHRlTDgiiqKQ646MwVDGKIzDaNI+jn0ovIOS5UYoHfVUgA

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

这个采用类型对象的函数如何在 TS 中键入? 的相关文章

随机推荐