英文:
Typescript generic conditional-partial property without type assertion
问题
I understand your request to provide a translation without addressing the specific code issue. Here's the translation of the content you provided:
我正在尝试创建一种类型,该类型具有取决于传递的泛型参数的可选属性。类似于以下内容:
在上面的示例中,我希望在V
不是string
时,getValue
是必需的。在调用getPropsValue
时,类型提示的工作方式是正确的。问题出在getPropsValue
的return value
上,其中我得到了Type 'V' is not assignable to type 'string'
,尽管在它上面的条件中已经需要了它,但是这只在V
不是string
时才会发生。
我唯一能够使用的解决方法是将value
断言为string
。
我还尝试了多次更改部分类型定义和使用联合类型,但不幸的是结果是一样的。
我尝试的另一种解决方案是检查value
是否是string
类型,如果不是,只需返回空字符串。虽然这在这种情况下是有效的,但在返回值是泛型的情况下是不可能的。
我将经常使用PartialWhenExtends
,并且希望不必在每个地方都进行断言。
是否有一种方法可以编写部分类型,以便TypeScript可以正确推断类型?如果没有,是否有其他适用于这种情况的解决方案?
英文:
I am trying to create a type that has an optional property depending on the generic parameter passed. Something like:
type PartialWhen<Bool, T> = Bool extends true ? Partial<T> : T
type PartialWhenExtends<Base, Parent, T> = PartialWhen<Base extends Parent ? true : false, T>
type Props<V> = {
value: V
} & PartialWhenExtends<V, string, {
getValue: (item: V) => string
}>
function getPropsValue<V>({ value, getValue }: Props<V>): string {
if (getValue !== undefined) {
return getValue(value)
}
return value
}
const props1: Props<string> = {
value: '123'
}
console.log(getPropsValue(props1))
const props2: Props<number> = {
value: 123,
getValue: (val) => val.toString()
}
console.log(getPropsValue(props2))
const props3: Props<string> = {
value: 'abc',
getValue: (val) => val.concat(val)
}
console.log(getPropsValue(props3))
In the example above, I want the getValue
to be required when V
is not string
. When calling getPropsValue
, the typehint works correctly. The problem is in the return value
of getPropsValue
in which I got Type 'V' is not assignable to type 'string'
, even though it is required when V
is not string
and would have returned in the condition above it.
The only solution I am able to use to solve this is by asserting the value
as string
.
I also tried changing the partial types definition multiple times & using union types, but unfortunately the result is the same.
Another solution that I tried is to check if the value
is type of string
, if not, just returns an empty string. While this works in this case, it is not possible where the return value is a generic.
I will use the PartialWhenExtends
quite a lot, and would prefer to not do assertion everywhere.
Is there a way I can write the partial types so Typescript can correctly infer the type? If not, is there any other solution that can fit into this kind of use case?
答案1
得分: 0
Solution:
- 修复您的代码以正确处理
Props<string | number>
的情况 - 让您的代码明确检查字符串类型
type PartialWhen<Bool, T> = Bool extends true ? Partial<T> : T
type PartialWhenExtends<Base, Parent, T> = Base extends Parent ? Partial<T> : T
type Props<V> =
{ value: V } &
PartialWhenExtends<[V], [string], { getValue: (item: V) => string }>
function getPropsValue<V>({ value, getValue }: Props<V>): string {
if (getValue !== undefined) {
return getValue(value)
}
if (typeof value === 'string')
return value
throw new Error()
}
// 正确
getPropsValue<string>({ value: 'asd' })
// ^?
getPropsValue<'asd'>({ value: 'asd' })
// ^?
getPropsValue<string & { a: 1 }>({ value: 'asd' as any })
// ^?
// 错误
getPropsValue<string | number>({ value: 123 })
// ^?
getPropsValue<number>({ value: 123 })
// ^?
您的函数可以像这样调用:
getPropsValue<string | number>({} as any)
// ^?
function getPropsValue<string | number>({ value, getValue }: {
value: string | number;
} & ({
getValue: (item: string | number) => string;
} | Partial<{
getValue: (item: string | number) => string;
}>)): string
所以 TypeScript 正确指出您的代码有问题。
我已经将代码修改为无需断言的工作方式,如下:
function getPropsValue<V>(
data: V extends string
? { value: V, getValue?: undefined } | { value: V, getValue: (item: V) => string }
: { value: V, getValue: (item: V) => string }
): string
getPropsValue<number | string>({} as any)
// ^?
// function getPropsValue<string | number>(data: {
// value: string;
// getValue?: undefined;
// } | {
// value: string;
// getValue: (item: string) => string;
// } | {
// value: number;
// getValue: (item: number) => string;
// }): string
但目前它会在失去非拆分 { value: V, getValue?: undefined }
的情况下停止工作。
英文:
Solution:
- Fix your code to be correct for
Props<string | number>
case - Make your code explicitly check for a string
type PartialWhen<Bool, T> = Bool extends true ? Partial<T> : T
type PartialWhenExtends<Base, Parent, T> = Base extends Parent ? Partial<T> : T
type Props<V> =
{ value: V } &
PartialWhenExtends<[V], [string], { getValue: (item: V) => string }>
function getPropsValue<V>({ value, getValue }: Props<V>): string {
if (getValue !== undefined) {
return getValue(value)
}
if (typeof value === 'string')
return value
throw new Error()
}
// ok
getPropsValue<string>({ value: 'asd' })
// ^?
getPropsValue<'asd'>({ value: 'asd' })
// ^?
getPropsValue<string & { a: 1 }>({ value: 'asd' as any })
// ^?
// errors
getPropsValue<string | number>({ value: 123 })
// ^?
getPropsValue<number>({ value: 123 })
// ^?
Your function can be called as
getPropsValue<string | number>({} as any)
// ^?
function getPropsValue<string | number>({ value, getValue }: {
value: string | number;
} & ({
getValue: (item: string | number) => string;
} | Partial<{
getValue: (item: string | number) => string;
}>)): string
so TS is correct about your code being wrong.
I've got the code working without assertion as
function getPropsValue<V>(
data: V extends string
? { value: V, getValue?: undefined } | { value: V, getValue: (item: V) => string }
: { value: V, getValue: (item: V) => string }
): string
getPropsValue<number | string>({} as any)
// ^?
// function getPropsValue<string | number>(data: {
// value: string;
// getValue?: undefined;
// } | {
// value: string;
// getValue: (item: string) => string;
// } | {
// value: number;
// getValue: (item: number) => string;
// }): string
but at the moment it loses non-split { value: V, getValue?: undefined }
it stops working.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论