如何提示 TypeScript 编译器两个泛型值必须相同?
How to hint to TypeScript compiler that two generic values must be the same?
我有一个简化的函数,它接受一个字符串文字和一个 returns { type: [mystringliteral] }
.
的回调
function myFunc<T>(type: T, callback: () => { type: T }): ReturnType<typeof callback> {
return { type };
}
除非我在调用时特别设置了 T
,否则 T
会被推断为 string
而不是字面值,并且 type
没有类型强制属性.
// successfully raises compilation error
const x = myFunc<'abc'>('abc', () => ({ type: 'def' }));
// does not raise an error
const y = myFunc('abc', () => ({ type: 'def' }));
我正在为第三方 npm 包编写声明文件,因此我无法调用任何巧妙的函数来向编译器提示正在发生的事情。
我猜我问的问题对于当前的 TypeScript 规范来说是完全不可能的(如果我们能做类似 <T extends string literal>
的事情就好了),但我想我会问。
这里有两个问题。第一个是让编译器推断字符串文字类型。为此,我们需要给T
添加一个string
的约束。这将向编译器提示我们需要文字类型。
但这还不够。只需将 T extends string
添加到签名中,编译器就会推断出 'abc' | 'def'
的并集,并且仍然不会产生错误。为了解决这个问题,我们必须让编译器知道 T
的第二次出现不应该用于推理。虽然有人提议 here 为此添加语法,但尚未实施。然而,我们可以使用 jcalz 建议的解决方法来获得所需的效果:
function myFunc<T extends string>(type: T, callback: () => { type: NoInfer<T> }): ReturnType<typeof callback> {
return { type };
}
type NoInfer<T> = [T][T extends any ? 0 : never];
// raises an error
const y = myFunc('abc', () => ({ type: 'def' }));
我有一个简化的函数,它接受一个字符串文字和一个 returns { type: [mystringliteral] }
.
function myFunc<T>(type: T, callback: () => { type: T }): ReturnType<typeof callback> {
return { type };
}
除非我在调用时特别设置了 T
,否则 T
会被推断为 string
而不是字面值,并且 type
没有类型强制属性.
// successfully raises compilation error
const x = myFunc<'abc'>('abc', () => ({ type: 'def' }));
// does not raise an error
const y = myFunc('abc', () => ({ type: 'def' }));
我正在为第三方 npm 包编写声明文件,因此我无法调用任何巧妙的函数来向编译器提示正在发生的事情。
我猜我问的问题对于当前的 TypeScript 规范来说是完全不可能的(如果我们能做类似 <T extends string literal>
的事情就好了),但我想我会问。
这里有两个问题。第一个是让编译器推断字符串文字类型。为此,我们需要给T
添加一个string
的约束。这将向编译器提示我们需要文字类型。
但这还不够。只需将 T extends string
添加到签名中,编译器就会推断出 'abc' | 'def'
的并集,并且仍然不会产生错误。为了解决这个问题,我们必须让编译器知道 T
的第二次出现不应该用于推理。虽然有人提议 here 为此添加语法,但尚未实施。然而,我们可以使用 jcalz 建议的解决方法来获得所需的效果:
function myFunc<T extends string>(type: T, callback: () => { type: NoInfer<T> }): ReturnType<typeof callback> {
return { type };
}
type NoInfer<T> = [T][T extends any ? 0 : never];
// raises an error
const y = myFunc('abc', () => ({ type: 'def' }));