typescript 如何使用泛型使参数类型相互依赖?

eeq64g8w  于 2023-01-06  发布在  TypeScript
关注(0)|答案(2)|浏览(166)

其思想是我可以提供2个参数,而第一个参数将提供一个字符串列表,对于第二个参数,我只想允许第一个参数中提供的值成为对象的允许键。
这就是我所尝试的。

declare function test<T extends readonly string[]>(a: T, b?: Record<typeof a[number], string>): unknown

test(['a', 'b'], {
  a: 'someValue',
  b: 'someOtherValue',
  c: 'thisShouldNotWork' // no error shown, as typescript only infers string[]
})

我知道你可以从一个元组创建一个UnionType,所以我的想法是这应该是可能的。对于Union to Tuple,它看起来像这样,并产生预期的行为:

const arg1 = ['a', 'b'] as const
type Arg1 = typeof arg1

declare function test<T>(a: T, b?: Record<T[number], string>): unknown

test<Arg1>(arg1, {
  a: 'someValue',
  b: 'someOtherValue',
  c: 'thisShouldNotWork'
})

我想要的是一个有点像我最初的方法的解决方案,其中我不需要为所有需要的参数提取UnionType。
任何帮助都是感激的!

oalqel3c

oalqel3c1#

这里的主要问题是,您希望编译器为作为参数传递给test的数组元素推断"someValue" | "someOtherValue"之类的字符串类型,但目前它推断的是string
编译器使用各种启发式来确定它是否应该推断文字类型。TypeScript 5.0很可能包含这样的功能:向generic类型参数声明添加const修饰符,以请求与constAssert中相同类型的文字首选项。这在microsoft/TypeScript#51865中实现。释放后,您可以获得所需的行为,如下所示:

// TS5.0+
declare function test<const T extends readonly string[]>(
// -----------------> ^^^^^
  a: T, b?: Record<T[number], string>): unknown

test(['a', 'b'], {
  a: 'someValue',
  b: 'someOtherValue',
  c: 'thisShouldNotWork' // error
}

现在最简单的方法是重构函数,使其仅在数组的元素类型中泛型,而不是在整个数组中泛型。当您将泛型类型参数约束为string时,它将倾向于推断类型参数的文本类型,如microsoft/TypeScript#10676中所述:

declare function test<K extends string>(
  a: readonly K[], b?: Record<K, string>
): unknown

test(['a', 'b'], {
  a: 'someValue',
  b: 'someOtherValue',
  c: 'thisShouldNotWork' // error
});

即使在TS5.0发布之后,我也可能会建议您这样做,除非您有某种原因关心输入数组的特定形状。
Playground代码链接

guz6ccqo

guz6ccqo2#

如果将文本类型赋给约束为文本生成类型(如string)的类型参数,则Typescript将推断文本类型
最简单的解决方案是为数组中的项使用类型参数,并将其约束为string:

declare function test<T extends string>(a: T[], b?: Record<T, string>): unknown

test(['a', 'b'], {
  a: 'someValue',
  b: 'someOtherValue',
  c: 'thisShouldNotWork' // no error shown, as typescript only infers string[]
})

Playground链接

相关问题