網站搜尋

typescript - 如何為聯合類型參數提供默認值而不出現「可能以約束的不同子類型實例化」的錯誤


作為我先前問題的後續...

在以下範例中,有沒有辦法為 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

播放

當我嘗試這個時:

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

,我收到這個錯誤:

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'.

我大致了解這個錯誤(1、2、3、4),但仍然不總是知道解決這個錯誤的最佳方法。

有沒有一個簡單的方法可以做到我正在嘗試做的事情?

似乎應該有一種簡單的方法來提供預設值,但也許預設值與通用型別約束並不太合得來?

泛型類型的預設類型?

我試了這個:

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

但當然還是出現相同的錯誤,因為如果提供了特定的 T,T 仍然可能是 defaultValue:

props<'defaultValue'>()

類型斷言?

我想過這麼做,這會編譯,但仍然無法防止 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"}

更複雜一點的東西嗎?

我仍然希望有一個簡單的解決方法,但如果沒有,也許更複雜的方案會有效?

也許可以使用一個映射表,將 類型 對應到該類型的預設 值?

作為靈感,也許像這樣的東西:

  • ‘CustomEnum.Case’ 可分配給型別 ‘T’ 的約束,但 ‘T’ 可能會使用約束 ‘CustomEnum’ 的不同子型別來實例化

  • https://github.com/microsoft/TypeScript/issues/29528#issuecomment-456804257 ?

答案#1

根據 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',
  }
}

播放

或者,如果我們想將 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',
  }
}

播放