繁体   English   中英

Typescript Generics:类型参数推断与通用默认类型参数

[英]Typescript Generics: Type argument inference vs Generic default type parameter

我正在努力找出Generic default type parameter的正确用例。

下面我创建了一个示例,我希望我的泛型 function 使用泛型默认类型参数而不是类型参数推断。 但是,它似乎不起作用。 我想我无法区分typescript generics这两个概念。

interface Person<K extends string | number | symbol = string> {
  addPerson: (key: K) => void;
}

interface StandardObject {
    [key: string]: string;
}

// sometimes I want keys to be string and sometimes string literal
declare function returnPerson<T extends StandardObject = StandardObject>(param: T): Person<keyof T>;

var obj = {
  name: "subrato",
  gender: "male"
}

const record = returnPerson(obj) 
record.addPerson('nam'); // I don't need this error, I want to ignore it 
// above line should not throw error but bcz of type argument inference it doesn't
// Why TS uses type argument inference here, why not generic default type parameter `StandardObject`?

const record2 =  returnPerson<StandardObject>(obj);
// here TS doesn't yell 
// since we explicit define object type argument whose key's would be string
// I don't want to be explicit to ignore the string literal type
record2.addPerson("nam");

const record3 = returnPerson<typeof obj>(obj);
// I want obj key should accept string literal constant only If I have given the type argument like in this record3 example.
record3.addPerson("nam")

游乐场链接

一般问题:当 TS 使用 generics 默认类型参数而不是类型参数推断时?

我的要求是我的通用 function returnPerson接受 object 其键类型有时是字符串,有时是字符串文字。

TL;DR当您使用泛型类型参数作为 function 参数的类型时,类型信息将从该参数的 position 中使用的参数类型推断出来。


考虑以下示例 - 它与您的问题基本相同,除了它包含您的 function 声明的实现并且更改了几个名称,但是,重要部分是相同的(类型泛型)。

TS游乐场

type PersonAdder<K extends PropertyKey = string> = {
  addPerson: (key: K) => void;
};

type StringObject = Record<string, string>;

function makePersonAdder <T extends StringObject = StringObject>(param: T): PersonAdder<keyof T> {
// Is the same as no default type parameter because inference always takes place:
// function makePersonAdder <T extends StringObject>(param: T): PersonAdder<keyof T> {
  return {
    addPerson (key) {
      console.log(key);
    },
  };
};


// Use:

const obj = {
  name: "subrato",
  gender: "male",
};

const a1 = makePersonAdder(obj); // PersonAdder<"name" | "gender">
a1.addPerson('name'); // ok
a1.addPerson('gender'); // ok
a1.addPerson('nam'); /*
             ~~~~~
Argument of type '"nam"' is not assignable to parameter of type '"name" | "gender"'.(2345) */

const a2 = makePersonAdder<typeof obj>(obj); // PersonAdder<"name" | "gender"> (identical to a1, original keys are inferred)
a2.addPerson('nam');
//           ~~~~~
// same error as with a1


const a3 = makePersonAdder(obj as StringObject); /* PersonAdder<string>
                               ^^^^^^^^^^^^^^^
Use a type assertion so that the inferred keys are simply 'string' */

a3.addPerson('name'); // ok
a3.addPerson('gender'); // ok
a3.addPerson('nam'); // ok
a3.addPerson('any other string'); // ok
a3.addPerson(42); /*
             ~~
Argument of type 'number' is not assignable to parameter of type 'string'.(2345) */

const a4 = makePersonAdder<StringObject>(obj); // PersonAdder<string> (identical to a3)
a4.addPerson('any other string'); // ok
a4.addPerson(42); /*
             ~~
Argument of type 'number' is not assignable to parameter of type 'string'.(2345) */

const a5 = makePersonAdder(obj as any); // PersonAdder<string | number | symbol>
a5.addPerson('any other string'); // ok
a5.addPerson(42); // ok

在 function makePersonAdder中,使用了泛型类型参数T

T既用作可作为参数param ( extends StringObject ) 的参数提供的内容的约束,又用作用于保存为参数param提供的参数的推断类型的变量。 因为返回类型使用从T ( PersonAdder<keyof T> ) 推断的类型信息,所以在创建返回类型时,参数值的类型总是用于推断。

在上面的代码中,有多个示例用法:

  • a1obj的类型推断键,所以返回类型是PersonAdder<"name" | "gender"> PersonAdder<"name" | "gender">

  • a2是通过为T提供精确类型创建的,因此不会根据提供的参数进行推断。 相反,您提供的参数必须扩展提供的类型。 在这种情况下,它们是相同的类型,所以就像a1一样,返回类型是PersonAdder<"name" | "gender"> PersonAdder<"name" | "gender"> (从typeof obj推断)。

  • a3在提供的参数上使用类型断言,以便编译器从断言类型而不是原始参数的类型推断。 这导致返回类型为PersonAdder<string>

  • a4a2发生的情况相同,只是这次为T提供的手动类型是StringObject ,因此返回类型推断出StringObject的键(它们是string ),从而产生PersonAdder<string>

  • a5再次使用类型断言,这次断言obj是类型any any是一种特殊的顶级类型,您可以将其视为本质上允许任何类型信息。 因为这种类型非常广泛,没有什么可以限制推理,所以使用所有允许的类型,导致PersonAdder<string | number | symbol> PersonAdder<string | number | symbol> PersonAdder<string | number | symbol>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM