英文:
Typescript generic conditional does not precise the type
问题
这是一段代码(playground,Typescript 5.0.4),它接受一个泛型函数,并根据类型执行某些操作:
type TypeToValue = {
one: number;
two: string;
};
function MyFunction<T extends keyof TypeToValue>(
value: TypeToValue[T],
type: T,
) {
if (type === 'one') return value.toFixed(2); // 报错 "Property 'toFixed' does not exist on type 'string | number'."
if (type === 'two') return value.toString()
throw Error('invalid type')
}
然而,我期望它不会报错,因为Typescript应该意识到在type === 'one'
之后,泛型T
会被推断为'one'
,因此value
的类型应该是number
。
是否有一种方法可以自动推断类型?
谢谢!
英文:
Here is a piece of code (playground, Typescript 5.0.4) that takes a generic function, and do some operation depending on the type:
type TypeToValue = {
one: number;
two: string;
};
function MyFunction<T extends keyof TypeToValue>(
value: TypeToValue[T],
type: T,
) {
if (type === 'one') return value.toFixed(2); // fail "Property 'toFixed' does not exist on type 'string | number'."
if (type === 'two') return value.toString()
throw Error('invalid type')
}
However, I expect it not to fail because Typescript should be aware that after type === 'one'
, then the generic T
is 'one'
hence value is typed as number
.
Is there a workaround to have the type automatically inferred?
Thanks !
答案1
得分: 1
我认为推断不够智能,无法理解正确的类型。不过,这只是我的个人意见。
无论如何,这里有一个稍微不同的版本,在这个版本中,它只是在运行时评估类型。这也确保了正确的推断。
type TypeToValue = {
one: number;
two: string;
};
function MyFunction<T extends keyof TypeToValue>(
value: TypeToValue[T],
// type: T,
) {
if (typeof value === 'number') return value.toFixed(2);
if (typeof value === 'string') return value.toString()
throw Error('invalid type')
}
let s: string;
s = MyFunction<"one">(123.456);
console.log(s); //打印 "123.46"
s = MyFunction<"two">("hello");
console.log(s); //打印 "hello"
也不需要第二个参数。
英文:
I think the inference is not smart enough to understand the proper type. However, that's just my humble opinion.
Anyway, here is a slightly different version, where it simply evaluates the type at runtime. That also ensures the proper inference.
type TypeToValue = {
one: number;
two: string;
};
function MyFunction<T extends keyof TypeToValue>(
value: TypeToValue[T],
// type: T,
) {
if (typeof value === 'number') return value.toFixed(2);
if (typeof value === 'string') return value.toString()
throw Error('invalid type')
}
let s: string;
s = MyFunction<"one">(123.456);
console.log(s); //prints "123.46"
s = MyFunction<"two">("hello");
console.log(s); //prints "hello"
Also no need of the second argument.
答案2
得分: 0
你需要为你的任务使用更通用的东西。
interface Interface1 {
a: string;
b: number;
}
interface Interface2 {
c: boolean;
d: any;
}
function isInterface<T>(value: any): value is T {
if (typeof value !== 'object' || value === null) {
return false;
}
for (const key in value) {
if (value[key] === undefined) {
return false;
}
}
return true;
}
type TypeToValue = number | string | Interface1 | Interface2;
function MyFunction(t: TypeToValue) {
if (typeof t === "number") {
return t.toFixed(2);
}
if (typeof t === "string") {
return t.toString();
}
if (isInterface<Interface1>(t)) {
console.log('processing obj of type Interface1');
return t; // or do something with object of type Interface1
}
if (isInterface<Interface2>(t)) {
console.log('processing obj of type Interface2');
return t; // or do something with object of type Interface2
}
throw new Error("Invalid type");
}
// test
const obj1 = { a: 'hello', b: 100 };
const obj2 = { c: true, d: 'world' };
console.log(MyFunction(100));
console.log(MyFunction("one hundred"));
console.log(MyFunction(obj1));
console.log(MyFunction(obj2));
英文:
You need something more generic for your task. Playground
interface Interface1 {
a: string;
b: number;
}
interface Interface2 {
c: boolean;
d: any;
}
function isInterface<T>(value: any): value is T {
if (typeof value !== 'object' || value === null) {
return false;
}
for (const key in value) {
if (value[key] === undefined) {
return false;
}
}
return true;
}
type TypeToValue = number | string | Interface1 | Interface2;
function MyFunction(t: TypeToValue) {
if (typeof t === "number") {
return t.toFixed(2);
}
if (typeof t === "string") {
return t.toString();
}
if (isInterface<Interface1>(t)) {
console.log('processing obj of type Interface1');
return t; // or do something with object of type Interface1
}
if (isInterface<Interface2>(t)) {
console.log('processing obj of type Interface2');
return t; // or do something with object of type Interface2
}
throw new Error("Invalid type");
}
// test
const obj1 = { a: 'hello', b: 100 };
const obj2 = { c: true, d: 'world' };
console.log(MyFunction(100));
console.log(MyFunction("one hundred"));
console.log(MyFunction(obj1));
console.log(MyFunction(obj2));
答案3
得分: 0
感谢 @jcalz 提供了一个有效的解决方法(示例):
type TypeToValue = {
one: number;
two: string;
};
type MyFunctionArgs = { [K in keyof TypeToValue]: [value: TypeToValue[K], type: K] }[keyof TypeToValue]
function MyFunction(...[value, type]: MyFunctionArgs) {
if (type === 'one') return value.toFixed(2); // okay
if (type === 'two') return value.toString()
throw Error('invalid type')
}
英文:
Thank you @jcalz for providing a valid workaround (playground):
type TypeToValue = {
one: number;
two: string;
};
type MyFunctionArgs = { [K in keyof TypeToValue]: [value: TypeToValue[K], type: K] }[keyof TypeToValue]
function MyFunction(...[value, type]: MyFunctionArgs) {
if (type === 'one') return value.toFixed(2); // okay
if (type === 'two') return value.toString()
throw Error('invalid type')
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论