为什么打字稿通用推断优先考虑参数而不是赋值?

时间:2019-12-21 11:17:40

标签: typescript generics typescript-typings type-inference typescript-generics

Typescript泛型推断优先考虑参数overs的分配。因为它优先考虑参数,所以即使我将其分配给类型参数设置为对象接口的变量,所有参数道具也将自动转换为类型unknown

interface Person {
  name: string;
  age: number;
  id: string;
}

interface Client {
  person: Person;
}

class FormField {  }

class FormFieldGroup<T> {
  constructor(private props: { category: string, questions: FormRelation<Required<T>> }) {}
}

type Primitives = string | number | symbol | bigint | undefined | null;

type FormRelation<T> = {
  [K in keyof T]: T[K] extends Primitives ? FormField : FormFieldGroup<T[K]>;
}

abstract class CRUDComponent<D> {
  public abstract clientQuestions: FormRelation<D>
}

class ClientComponent extends CRUDComponent<Client> {
  public clientQuestions: FormRelation<Client> = {
    person: new FormFieldGroup({
      category: "Client",
      questions: {
        name: new FormField(),
        age: new FormField(),
        id: new FormField(),
      }
    })
  }
}

VScode: Cannot assign FormQuestionGroup<{name: unknown, age: unknown, id: unknown}> to FormQuestionGroup<Person>.

在Java中,菱形运算符会自动推断类型以匹配分配类型参数。但是,出于可读性考虑,Typescript不包括菱形运算符。我正在使用Typescript 3.7,只是想知道是否有解决此错误的方法,而不是指定类型。

另外,当我将props设置为一个空对象时,编译器也可以将Generic推断为正确的接口。

Typescript Playground

2 个答案:

答案 0 :(得分:0)

您可以在T中为FormQuestionGroup使用recursive type constraint来对类型进行建模:

// use recursive type constraint for T here
class FormQuestionGroup<T extends FormQuestionGroupProps<T["questions"]>> {
  constructor(public props: T) { }
}

interface FormQuestionGroupProps<T> {
  questions: FormRelation<T>;
}

type FormRelation<T> = {
  [K in keyof T]: FormField;
}

class FormField { }

测试(Playground):

const group = new FormQuestionGroup({
  questions: {
    name: new FormField(),
    age: new FormField()
  }
});

group.props.questions.age // FormField, works now
group.props.questions.name // FormField, works now

为什么我们以前得到unknown类型? TS显然无法正确推断此星座中的T

declare function foo<T>(t: { [K in keyof T]: string }): void
foo({ name: "foo", a: "bar" })
//  foo<{ name: unknown; a: unknown; }>(t: { name: string; a: string; }): void

PS:我坚持使用您的第3个修订代码示例,该示例仅与推断的unknown类型有关。最新的代码创建了一个全新的(编译)错误-当您传入T[K] extends Primitives ? FormField : FormFieldGroup<T[K]>时,FormFieldGroup<T[K]>始终会解析为FormField,因为它不是原始的。

答案 1 :(得分:0)

您只需要用type NoInfer<T> = [T][T extends any ? 0 : never]包装类型即可禁用参数推断。