[英]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 声明的实现并且更改了几个名称,但是,重要部分是相同的(类型泛型)。
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>
) 推断的类型信息,所以在创建返回类型时,参数值的类型总是用于推断。
在上面的代码中,有多个示例用法:
a1
从obj
的类型推断键,所以返回类型是PersonAdder<"name" | "gender">
PersonAdder<"name" | "gender">
。
a2
是通过为T
提供精确类型创建的,因此不会根据提供的参数进行推断。 相反,您提供的参数必须扩展提供的类型。 在这种情况下,它们是相同的类型,所以就像a1
一样,返回类型是PersonAdder<"name" | "gender">
PersonAdder<"name" | "gender">
(从typeof obj
推断)。
a3
在提供的参数上使用类型断言,以便编译器从断言类型而不是原始参数的类型推断。 这导致返回类型为PersonAdder<string>
。
a4
与a2
发生的情况相同,只是这次为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.