如何返回预先填充了一些基本对象的值的扩展对象?



我有一个基本接口,另一个接口对它进行了扩展。我正在尝试编写一个函数,它将从基本接口获得一些参数,其中一些参数将被预填充。如果基接口中不存在任何新属性,则也会将其作为参数接受,因为它们无法预先填充。

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. */

实现这一目标的正确方法是什么?

游乐场连接

据我所知,您的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。您可以要求将这些属性包含在道具中,但您已经说过要预先填充这些属性。

我已经写了这篇文章,其中每个thing接口都有一组单独的默认值,并将它们传递给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>>

在这一点上,我仍然得到'T' could be instantiated with a different subtype of constraint 'Foo'消息,但我满足于用as T来隐藏它,因为我无法传递隐藏在NarrowedDefaults<Bar>Props<Bar>中的无效值。

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'});

游乐场链接

最新更新