在大多数情况下,具有泛型类型的函数可以从参数推断泛型类型。但是,如果参数是泛型类型既是参数又是返回值的函数,则有时不会推断出泛型类型。
一个愚蠢的例子,有一个用于存储项目的类,其中项目的一些属性是自动生成的(例如,一个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
,则返回的创建者函数将只需要name
和email
。
然而,在某些情况下,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=未知