根据传递函数的返回值推断函数泛型类型U



在大多数情况下,具有泛型类型的函数可以从参数推断泛型类型。但是,如果参数是泛型类型既是参数又是返回值的函数,则有时不会推断出泛型类型。

一个愚蠢的例子,有一个用于存储项目的类,其中项目的一些属性是自动生成的(例如,一个ID是自动产生的数据库(:

/**
* Stores items of type T
*/
class Store<T> {
/**
* Return a function that creates items from supplied partial items merged with
* attributes U auto-generated by a generator function
*/
itemCreator<U>(
generate: (item: Omit<T, keyof U>) => U
): (item: Omit<T, keyof U>) => Omit<T, keyof U> & U {
return item => ({...item, ...generate(item)});
}
}
type Person = {
id: string;
name: string;
email: string;
age?: number;
};

因此,如果创建一个Store<Person>并提供一个生成器来自动生成id,则返回的创建者函数将只需要nameemail

然而,在某些情况下,U并不是推断出来的:

作品:

const create = new Store<Person>()
.itemCreator(() => ({id: 'ID', extra: 42}));
// U is {id: string, extra: number}, `create` only needs to provide `name` and `email` :)
const person = create({name: 'John', email: 'john.doe@foo.com'}); // creates person with extra

不起作用:

const create = new Store<Person>()
.itemCreator(item => ({id: 'ID', extra: 42}));
// U is now unknown, meaning the `create` function must provide complete `Person` objects :(
const person = create({name: 'John', email: 'john.doe@foo.com'}); // does not compile

使用显式<U>:

const create = new Store<Person>()
.itemCreator<{id: string, extra: number}>((item) => ({id: 'ID', extra: 42}));
const person = create({name: 'John', email: 'john.doe@foo.com'}); // creates person with extra

现在,将部分项传递给生成器的原因是,一些自动生成的属性可能依赖于其他属性(例如,作为email属性的哈希生成的id(:

const creator = new Store<Person>()
.itemCreator(item => ({id: hash(item.email)}))

所以,我的问题是,如果提供了generate函数的参数,为什么推断U失败?TypeScript只是使用第一个找到的U实例吗?原因是什么?generate函数返回U,因此如果它看到{id: string}被返回,则可能会认为U也存在于generate参数Omit类型中这一事实应该无关紧要?

有办法解决这个问题吗?

@JHH,@Linda Paiste您认为下一个解决方案是什么?:


class Store<T> {
itemCreator<U>(
generate: <P = Omit<T, keyof U>>(item: P) => U
): (item: Omit<T, keyof U>) => Omit<T, keyof U> & U {
return item => ({ ...item, ...generate(item) });
}
}
type Person = {
id: string;
name: string;
email: string;
age?: number;
};

const create = new Store<Person>()
.itemCreator(item => {
const x = item // Omit<Person, "id" | "extra">
return ({ id: 'ID', extra: 42 })
});
const person = create({ name: 'John', email: 'john.doe@foo.com' });

TS似乎很难推断出item参数,所以我用默认值定义了额外的泛型。

在这里,在我的博客中,你可以找到更多有趣的回调打字

在这里,你可以找到Titian Cernicova Dragomir 对这种行为的解释

如果有一个参数,检查器需要在检查主体之前决定U,因为没有未知的推理来源。然后,它检查身体,但不会返回U = ret type重试,它只是检查返回的compa。其中U=未知