如何提示 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' }));