在使用带有泛型的区分联合时,我需要编写一些冗余代码,这也可能对类型安全性有害。
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
function<T extends Shape> fetch(type: Shape['kind'], id: string): T {
// fetch data from database via id
}
问题是函数fetch
中存在多余的类型说明,我可以使用Shape['kind']
将类型限制为'square' | 'rectangle' | 'circle'
,但是像fetch<Square>('circle', 'some-id')
这样的调用仍会编译。如何解决这个问题呢?有什么方法可以定义功能,例如以下版本之一?
fetch<T extends Shape>(type: T['kind'], id: string):T
fetch<T extends Shape['kind']>(type: T, id: string): SomeMagic<T>
和SomeMagic<T>
可以帮助编译器找到正确的类型,例如SomeMagic<'square'>
在编译时推断Square
吗?答案 0 :(得分:2)
我建议您采用这种“版本2”方法:
Python -V
“魔术”是定义为in the standard library的declare function fetch<T extends Shape['kind']>(
type: T,
id: string
): Extract<Shape, {kind: T}>;
fetch<'square'>('circle', 'some-id'); //error
const shape = fetch('rectangle', 'some-id'); // shape is a Rectangle
类型的函数,它是conditional type,它从联合中提取匹配元素。
您可以采用“版本1”方法,但我不建议这样做:
Extract
请注意,declare function fetch<T extends Shape>(type: T['kind'], id: string): T;
fetch<Square>('circle', 'some-id'); //error
const shape = fetch('rectangle', 'some-id'); // shape is a Shape
参数是type
,而不是T['kind']
。如前所述,它可以解决您的问题,但是如果您允许编译器从参数中推断出Shape['kind']
,则最终只能推断出T
,因为Shape
对于T['kind']
。
无论如何,希望能有所帮助。祝你好运。
答案 1 :(得分:0)
我们需要处理的第一件事是将形状与它们各自的种类绑定在一起。您原来的解决方案:
/**
* Bad — doesn't bind the `Shape` with its `kind`. It allows calling `fetch<Square>('circle', 'foo')`.
*/
declare function fetch<T extends Shape>(type: Shape['kind'], id: string): T;
相反,要告诉TypeScript type
不仅需要任何种类,而且还必须完全是我们当前正在考虑的形状。
/**
* Better: the `kind` is bound with its corresponding `Shape`. The downside: The exact return type is not inferred.
*/
declare function fetch<T extends Shape>(type: T['kind'], id: string): T;
const oups = fetch<Square>('circle', 'foo'); // $ExpectError
const shape = fetch('circle', 'foo'); // $ExpectType Shape
这更好,但是返回类型只是一个Shape
。通过为您的函数指定重载,我们可以做得更好:
/**
* Using overloads can help you determine the exact return type.
*/
declare function fetch(type: 'circle', id: string): Circle;
declare function fetch(type: 'square', id: string): Square;
declare function fetch(type: 'rectangle', id: string): Rectangle;
const circle = fetch('circle', 'foo'); // $ExpectType Circle
这将为您提供确切的返回类型,但要付出编写更多代码的代价。也许还会有人争辩说存在某种冗余-形状与其种类之间的联系已经封装在其接口中,因此以过载的形式重复它似乎并不完美。