我有以下类型
type ItemDefaultType = object | null | string
interface ItemToString<Item = ItemDefaultType> {
(item: Item): string;
}
interface AutosuggestState<Item = ItemDefaultType> {
highlightedIndex: number | null
inputValue: string | null
isOpen: boolean
selectedItem: Item
}
interface AutosuggestProps<Item = ItemDefaultType>
extends Partial<AutosuggestState<Item>> {
itemToString?: ItemToString<Item>;
initialSelectedItem?: Item;
initialInputValue?: string | null;
initialHighlightedIndex?: number | null;
initialIsOpen?: boolean;
defaultHighlightedIndex?: number | null;
defaultIsOpen?: boolean;
}
我有以下输入代码
function capitalizeString(string: string) {
return `${string.slice(0, 1).toUpperCase()}${string.slice(1)}`
}
const defaultStateValues: AutosuggestState<null> = {
highlightedIndex: -1,
isOpen: false,
selectedItem: null,
inputValue: ''
}
function getDefaultValue<
P extends AutosuggestProps,
K extends keyof AutosuggestState
>(
props: P,
statePropKey: K
) {
const defaultPropKey = `default${capitalizeString(statePropKey)}`
if (defaultPropKey in props) {
return props[defaultPropKey as K] // assert here
}
return defaultStateValues[statePropKey]
}
function getInitialValue<
P extends AutosuggestProps,
K extends keyof AutosuggestState
>(
props: P,
statePropKey: K
) {
if (statePropKey in props) {
return props[statePropKey]
}
const initialPropKey = `initial${capitalizeString(statePropKey)}`
if (initialPropKey in props) {
return props[initialPropKey as K] // assert here
}
return getDefaultValue(props, statePropKey)
}
在做
declare const props: AutosuggestProps;
const selectedItem = getInitialValue(props, 'selectedItem');
selectedItem
显示返回类型object | null | string | undefined
。但是根据getInitialValue
和getDefaultValue
函数的性质,可以肯定的是,至少selectedItem
会从defaultStateValues
对象返回默认值。
如何编写更有针对性的基于条件类型的函数签名,getInitialValue
并getDefaultValue
尝试根据输入缩小到特定输出,即selectedItem
具有类型ItemDefaultType
而不是object | null | string | undefined
(实际上是ItemDefaultType | undefined
)
有没有更好的方法来编写类型和函数?例如,在这个问题中——
回答者建议可能不需要类型断言 -
如果您可以重构您的接口,以便将默认值和初始值存储在名为default和initial 的属性中,这些属性本身就是持有相同键属性的对象,那么您可以让编译器弄清楚
是否可以对类型定义和接口进行其他结构更改?
注意:如果您只有时间回答一个,请回答第一个。
带有预写代码的Typescript Playground
我处理这个问题的第一次尝试是进行提到的结构重构,并使用Exclude
实用程序类型来表示undefined
在您防范undefined
值时不可能输出的约束。
所以重构看起来像这样:
interface AutosuggestProps<Item = ItemDefaultType>
extends Partial<AutosuggestState<Item>> {
itemToString?: ItemToString<Item>;
initial?: {
highlightedIndex?: number | null;
inputValue?: string | null;
isOpen?: boolean;
selectedItem?: Item;
}
default?: {
highlightedIndex?: number | null;
inputValue?: string | null;
isOpen?: boolean;
selectedItem?: Item;
}
}
功能如下所示:
function getDefaultValue<
P extends AutosuggestProps,
K extends keyof AutosuggestState
>(
props: P,
statePropKey: K
) {
if (props.default && typeof props.default[statePropKey] !== "undefined") {
const ret = props.default[statePropKey]
return ret as Exclude<typeof ret, undefined>;
}
return defaultStateValues[statePropKey]
}
function getInitialValue<
P extends AutosuggestProps,
K extends keyof AutosuggestState
>(
props: P,
statePropKey: K
) {
if (statePropKey in props && typeof props[statePropKey] !== "undefined") {
const ret = props[statePropKey];
return ret as Exclude<typeof ret, undefined>;
}
if (props.initial && typeof props.initial[statePropKey] !== "undefined") {
const ret = props.initial[statePropKey];
return ret as Exclude<typeof ret, undefined>;
}
return getDefaultValue(props, statePropKey)
}
这给出了以下结果:
declare const props: AutosuggestProps;
const selectedItem = getInitialValue(props, 'selectedItem');
// const selectedItem: ItemDefaultType
也许这对你来说就足够了。
如果您需要一个条件类型签名来尝试预测每个函数中实际发生的分支并输出更紧密的类型,这将以函数实现内部的类型安全性降低为代价,因为编译器不执行此操作自动分析,也不能验证手动条件类型是否与函数的结果相匹配。
无论如何,它可能看起来像这样:
type IdxWithDefault<T, K extends keyof any, D = never> = T extends any ? K extends keyof T ?
Exclude<T[K], undefined> | (undefined extends T[K] ? D : never) : D : never
该类型IdxWithDefault<T, K, D>
尝试查找关键K
的类型T
。如果成功而属性不成功undefined
,则计算结果为T[K]
。否则,它的计算结果为D
。它分布在联合上,所以如果T[K]
是string | undefined
,那么输出将是string | D
。
现在函数签名可以使用IdxWithDefault
:
function getDefaultValue<
P extends AutosuggestProps,
K extends keyof AutosuggestState
>(
props: P,
statePropKey: K
): IdxWithDefault<
P["default"], K, IdxWithDefault<
typeof defaultStateValues, K
>
>
function getDefaultValue(props: AutosuggestProps, statePropKey: keyof AutosuggestState) {
if (props.default && typeof props.default[statePropKey] !== "undefined") {
return props.default[statePropKey]
}
return defaultStateValues[statePropKey]
}
function getInitialValue<
P extends AutosuggestProps,
K extends keyof AutosuggestState
>(
props: P,
statePropKey: K
): IdxWithDefault<
P, K, IdxWithDefault<
P["initial"], K, IdxWithDefault<
P["default"], K, IdxWithDefault<
typeof defaultStateValues, K>
>
>
>;
function getInitialValue(props: AutosuggestProps, statePropKey: keyof AutosuggestState) {
if (statePropKey in props && typeof props[statePropKey] !== "undefined") {
return props[statePropKey];
}
if (props.initial && typeof props.initial[statePropKey] !== "undefined") {
return props.initial[statePropKey];
}
return getDefaultValue(props, statePropKey)
}
测试它...这是一样的,因为props
只知道是AutosuggestProps
.
declare const props: AutosuggestProps;
const selectedItem = getInitialValue(props, 'selectedItem');
但以下输出string
类型:
getInitialValue({
selectedItem: "wheee"
}, "selectedItem"); // string
我想这就是我所能做到的。如果您发现边缘情况不符合您的预期,您可以尝试使用嵌套IdxWithDefault
类型。
好的,希望有帮助;祝你好运!
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句