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

时间:2021-03-19 09:57:30

标签: typescript typescript-generics inferred-type

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

带有用于存储项目的类的简化示例,其中项目的某些属性是自动生成的(例如自动生成 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

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

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

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

有没有办法解决这个问题?

1 个答案:

答案 0 :(得分:3)

@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 参数,所以我用默认值定义了额外的泛型。

Here,在我的博客中,你可以找到更多有趣的回调类型

Here 您可以找到 Titian Cernicova Dragomir 对此行为的解释

<块引用>

如果有参数,checker 需要在检查 body 之前决定 U,因为没有推断的来源,它是未知的。然后它检查正文,但不会返回再次尝试使用 U = ret type 它只是检查返回 compa。 U = 未知

相关问题