如何为联合类型参数提供默认值而不得到“可以使用不同的约束子类型实例化”

Tyl*_*ick 5 typescript typescript-generics

作为我之前问题的后续......

valueProp有没有办法在以下示例中提供默认值?

type ValueType = 'value' | 'defaultValue'

type Props<T extends ValueType> =
  Record<T, string> 
  & { other: string }

function props<T extends ValueType>(valueProp: T): Props<T> {
  return {
    [valueProp]: 'value',
    other: 'other',
  } as Props<T>
}

let { defaultValue, other } = props('defaultValue') // ok
let { value } = props('value') // ok
Run Code Online (Sandbox Code Playgroud)


当我尝试这个时:

function props<T extends ValueType>(valueProp: T = 'value'): Props<T> {
  return {
    [valueProp]: 'value',
    other: 'other',
  } as Props<T>
}
Run Code Online (Sandbox Code Playgroud)

,我收到此错误:

Type '"value"' is not assignable to type 'T'.
  '"value"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'ValueType'.
Run Code Online (Sandbox Code Playgroud)

我一般了解此错误(1 , 2 , 3 , 4),但仍然不总是知道解决错误的最佳方法。

有没有一种简单的方法可以完成我想做的事情?

似乎应该有一种简单的方法来提供默认值,但也许默认值与泛型类型约束不能很好地混合?

泛型类型的默认类型?

我试过这个:

function props<T extends ValueType = 'value'>(valueProp: T = 'value'): Props<T> {
  return {
    [valueProp]: 'value',
    other: 'other',
  } as Props<T>
}
Run Code Online (Sandbox Code Playgroud)

但当然得到了同样的错误,因为defaultValue如果提供了特定的T 仍然可能是T

props<'defaultValue'>()
Run Code Online (Sandbox Code Playgroud)

类型断言?

我想过这样做,它可以编译,但仍然不能阻止valueProp与 T 的不同意见:

function props<T extends ValueType>(valueProp: T = 'value' as T): Props<T> {
  return {
    [valueProp]: 'value',
    other: 'other',
  } as Props<T>
}

console.log(props<'defaultValue'>())
// => {value: "value", other: "other"}
Run Code Online (Sandbox Code Playgroud)

有更复杂的事情吗?

我仍然希望有一个简单的解决方案,但如果没有,也许更复杂的东西可以工作?

也许使用从类型映射到该类型的默认值的映射表?

为了获得灵感,也许是这样的:

Tyl*_*ick 2

根据 Titian Cernicova-Dragomir 的评论,我们可以使用函数重载非常简单地做到这一点:

type ValueType = 'value' | 'defaultValue'

type Props<T extends ValueType> =
  Record<T, string> 
  & { other: string }

function props<T extends ValueType>(valueProp: T): Props<T>
function props(): Props<'value'>
function props(valueProp: ValueType | undefined = 'value') {
  return {
    [valueProp]: 'value',
    other: 'other',
  }
}
Run Code Online (Sandbox Code Playgroud)


或者,如果我们想valueProp从位置参数转移到伪命名参数(使用对象解构):

type PropsOptions<VT extends ValueType | undefined> = {
  valueProp?: VT
}

function props<VT extends ValueType>({valueProp}: PropsOptions<VT>): Props<VT>
function props(): Props<'value'>
function props({ valueProp = 'value' }: PropsOptions<ValueType | undefined> = {}) {
  return {
    [valueProp]: 'value',
    other: 'other',
  }
}
Run Code Online (Sandbox Code Playgroud)