我有一个基接口,另一个接口扩展了它。我正在尝试编写一个函数,它将从基接口中获取一些参数,一些参数将被预先填充。如果存在基接口中不存在的任何新属性,也会将其作为参数接受,因为它们不能预先填充。
interface Foo {
prop: number;
hidden: number;
}
interface Bar extends Foo {
prop: 1;
hidden: 2;
newProp: string;
}
const createThing = <T extends Foo>({
prop,
...rest
}: Omit<T, keyof Foo> & Pick<T, 'prop'>): T => {
return {
prop,
hidden: 3, // <-- I want this prefilled, but it must be 2, not 3.
...rest,
}; /* if I write `as T`, it works, but it allows me to pass 3 to the
`hidden` property, which will be wrong. */
/* but this whole return complains, that T could be instantiated with
a different subtype of constraint 'Foo'. */
};
const thing = createThing<Bar>({ prop: 1, newProp: 'foo' });
thing.hidden === 2; /* this will be false, because I returned 3
from the createThing function. */实现这一目标的正确方法是什么?
发布于 2020-09-25 09:46:35
据我所知,您的createThing<T>方法可以接受T (Partial<T>)的任何属性,并且它需要包含任何不属于Foo (Omit<T, keyof Foo>)的新属性。那是,
type Props<T extends Foo> = Partial<T> & Omit<T, keyof Foo>;当T与Foo具有相同的属性名称,但具有更具体的类型时,事情变得更加棘手。即。hidden必须为2,而不是任何number。您可以要求将这些属性包含在Props中,但您已经说过希望预先填充它们。
我写了这段代码,其中每个接口都有一组单独的默认值,然后将这些默认值传递给createThing。否则,它如何知道什么是适当的默认值,什么不是呢?
如果我理解正确的话,额外的属性不需要默认值,但是任何Foo接口属性都需要一个默认值,如果它们更具体的话。在这里,我使用一个映射类型来确定您需要哪些属性。
type NarrowedKeys<T extends Foo> = {
// there's probably a cleaner way to write this
[K in keyof T]: K extends keyof Foo ? Foo[K] extends T[K] ? never : K : never
}[keyof T];
type NarrowedDefaults<T extends Foo> = Pick<T, NarrowedKeys<T>>此时,我仍然得到了隐藏消息,但是我满足于用as T隐藏它,因为我不能传递NarrowedDefaults<Bar>或Props<Bar>中的无效值'T' could be instantiated with a different subtype of constraint 'Foo'。
const fooDefaults: Foo = {
prop: 1,
hidden: 5,
}
const createThing = <T extends Foo>(tDefaults: NarrowedDefaults<T>) => (props: Props<T>): T => {
return {
...fooDefaults,
...tDefaults,
...props,
} as T;
};
const createBar = createThing<Bar>({
prop: 1,
hidden: 2,
});
const thing = createBar({prop: 1, newProp: 'foo'});https://stackoverflow.com/questions/64056288
复制相似问题