请问typescript如何给泛型的参数指定默认值?

只是小丑
  • 36

例如

const arrToObj = <T extends Record<L | V, unknown>, L extends string, V extends string>(arr: T[], label:L = 'label' , value:V = 'value'): Record<string, T[L]>

提示如下错误

不能将类型“string”分配给类型“L”。
"string" 可赋给 "L" 类型的约束,但可以使用约束 "string" 的其他子类型实例化 "L"。ts(2322)
回复
阅读 1k
1 个回答

string 类型其实是个类型集合.
在 TS 中,有一种类型是使用一个具体的字符串来表示的,也就是字面量类型(Literal Types).
比如 type A = 'a' ,这里 A 就表示是一个字符串为 'a' 的类型,而不能是别的字符串.
这样的字符串有无数个,对应的这样的字面量类型有无数个,而 string 就是这些所有字符串字面量类型的集合.
当指定 L extends string 时,L 有可能是 label ,也有可能不是,一切都是未知的.
这里会报错断原因,就是因为 L 需要在使用时去指定或者推断出来的,并不一定是 lable ,所以不能在不知道 L 具体的类型时就去指定它实际的值.

除了字符串字面量类型,每个基础类型都有对应的字面量类型,详细可以点击后面的参考链接,去阅读文档.

如果只是想要消除报错的话,可以直接用 as 断言成对应的类型.如下:

const arrToObj = <
  T extends Record<L | V, unknown>,
  L extends string,
  V extends string,
>(
  arr: T[],
  label: L = 'label' as L,
  value: V = 'value' as V,
): Record<string, T[L]> => {
  return arr as any
}

不过这样会有一个问题,就是当使用默认值是,返回值里的 T[L] 不会被正确推断出来.

这个函数其实可以分为三种情况来看

  1. 没有默认值,传入三个参数
  2. value 有默认值,传入两个参数
  3. label 和 value 有默认值,传入一个参数

这时候使用函数重载来实现最合适了.在使用时也能正确推断出 T[L] 类型了.如下:

function arrToObj<
  T extends Record<L | V, unknown>,
  L extends string,
  V extends string,
>(arr: T[], label: L, value: V): Record<string, T[L]>
function arrToObj<T extends Record<L | 'value', unknown>, L extends string>(
  arr: T[],
  label: L,
): Record<string, T[L | 'value']>
function arrToObj<T extends Record<'label' | 'value', unknown>>(
  arr: T[],
): Record<string, T['label' | 'value']>
function arrToObj(
  arr: Record<string, unknown>[],
  label = 'label',
  value = 'value',
) {
  return arr as any
}

TS Playground


你知道吗?

宣传栏