[英]Why does the property of an object used in the union has the type any?
我有一個包含 3 個道具的組件: toValue
、 fromValue
、 children
。 其中前 2 個是可選的,但如果指定了其中一個,則還必須指定另一個。 children
prop 是一個具有inputProps
參數的函數,它應該等於toValue
函數的返回類型 ( TInputValue
),如果指定的話,或者TStateValue
。
我寫了下面的代碼,但我不明白為什么在指定toValue
函數的情況下inputProps
是any
。 如何解決?
interface IP<T> {
value: T
onChange: (value: T) => void
}
interface A<TStateValue> {
toValue?: never
fromValue?: never
children: (inputProps: IP<TStateValue>) => null
}
interface B<TStateValue, TInputValue> {
toValue: (value: TStateValue) => TInputValue
fromValue: (value: TInputValue) => TStateValue
children: (inputProps: IP<TInputValue>) => null
}
type C<TStateValue, TInputValue> =
| A<TStateValue>
| B<TStateValue, TInputValue>
const propsWithoutToValue: C<number, string> = {
children: (inputProps) => null, // `inputProps` is `IP<number>`. Nice!
}
const propsWithToValue: C<number, string> = {
toValue: (value) => value.toString(), // The return type is a `string`, so the `inputProps` must be `IP<string>`
fromValue: (value) => Number(value),
children: (inputProps) => null, // `inputProps` is any, but must be `IP<string>`. How to fix it?
}
我已經為我要解決的問題創建了一個回購協議: https ://github.com/oxilor/form-types
快速解決方案:
interface IP<T> {
value: T
onChange: (value: T) => void
}
interface A<TStateValue> {
type:"A",
children(inputProps: IP<TStateValue>, a:1) : null
}
interface B<TStateValue, TInputValue> {
type: "B",
toValue: (value: TStateValue) => TInputValue
fromValue: (value: TInputValue) => TStateValue
children(inputProps: IP<TInputValue>) : null
}
type C<TStateValue, TInputValue> =
| B<TStateValue, TInputValue>
| A<TStateValue>
const propsWithoutToValue: C<number, string> = {
type: "A",
children: (inputProps) => null,
// ^?
}
const propsWithToValue: C<number, string> = {
type: "B",
toValue: (value) => value.toString(),
fromValue: (value) => Number(value),
children: (inputProps) => null,
// ^?
}
//========================觀察===========
A 點:如果您嘗試聯合 2 個具有不同參數類型的函數,則參數類型為any
重要更新:為什么函數“聯合”參數顯示為any
但實際上never
或其他
B 點:打字稿認為您嘗試聯合 2 個函數的原因是因為具有相同對象屬性的聯合函數的行為方式與兩個函數的聯合相同
這基本上意味着{b:((a:1)=>void)} | {b:((a:number)=>void)}
{b:((a:1)=>void)} | {b:((a:number)=>void)}
字面意思是{b:((a:1)=>void) | ((a:number)=>void)}
{b:((a:1)=>void) | ((a:number)=>void)}
有幾件事我無法解釋:
這是使用條件類型的替代方法。
我們這里有兩種通用類型。 類型決定了提供給children
的道具。 我將其稱為TChildProps
並將其放在首位,因為它始終存在。
然后是僅在情況B
中相關的類型TStateValue
。
在情況A
中,您的狀態可以直接提供給toValue
,因此不需要children
和fromValue
轉換器函數。
條件說如果狀態可以直接傳遞給children
( TStateValue extends TChildProps
)那么我們處於情況A
。 如果狀態是不同的類型,那么我們處於情況B
並且必須提供轉換器。
type C<TChildProps, TStateValue = TChildProps> =
TStateValue extends TChildProps ? A<TStateValue> : B<TStateValue, TChildProps>
C
上的泛型與您之前的泛型略有不同。
對於propsWithoutToValue
,我們只需要number
類型。
const propsWithoutToValue: C<number> = {
children: (inputProps) => null, // `inputProps` is `IP<number>`. Nice!
}
在propsWithToValue
的情況下,兩個泛型的順序現在顛倒了。
const propsWithToValue: C<string, number> = {
toValue: (value) => value.toString(),
fromValue: (value) => Number(value),
children: (inputProps) => null, // `inputProps` is `IP<string>`. We did it!
}
第一個通用類型參數string
將使inputProps
具有類型IP<string>
。
由於第二個泛型類型參數number
不能分配給string
,我們必須提供一個將number
轉換為string
的toValue
函數和一個將string
轉換為number
的fromValue
函數。
解決這個問題的一種方法是只聲明一種接口類型並將兩個屬性設置為可選,並且從不返回類型。 這里的一個問題是兩個鍵總是可選的,即 toValue 和 fromValue
interface IP<T> {
value: T
onChange: (value: T) => void
}
interface IProps<TStateValue, TInputValue> {
toValue?: (value: TStateValue) => TInputValue | never
fromValue?: (value: TInputValue) => TStateValue | never
children: (inputProps: IP<TInputValue>) => null
}
const propsWithoutToValue: IProps<number, string> = {
children: (inputProps) => null
}
const propsWithToValue: IProps<number, string> = {
toValue: (value) => value.toString(),
fromValue: (value) => Number(value),
children: (inputProps) => null
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.