通用联合类型的类型保护
Typeguard for generic union type
我创建了一个联合类型:
type RequestParameterType = number | string | boolean | Array<number>;
我有一个 class,它是一个 key/value 对,持有该联合类型:
class RequestParameter
{
constructor(name: string, value: RequestParameterType)
{
this.Name = name;
this.Value = value;
}
public Name: string;
public Value: RequestParameterType;
}
然后我可以创建一个 RequestParameter 数组来保存 keys/values:
let parameters: Array<RequestParameter> = new Array<RequestParameter>();
parameters.push(new RequestParameter("one", 1));
parameters.push(new RequestParameter("two", "param2"));
我的想法是我可以编写一个 GetParameter
函数来从该数组中输入 return 类型的值,实际上我可能会这样使用:
// should return number type, with value 1
let numberParam: number | undefined = this.GetParameter<number>("one", parameters);
// should return string type, with value "param2"
let stringParam: string | undefined = this.GetParameter<string>("two", parameters);
// should return undefined, because param named 'two' is not number type
let undefinedParam: number | undefined = this.GetParameter<number>("two", parameters);
但是我的函数获取类型化参数时遇到问题,因为我不知道如何检查泛型类型是否与值类型匹配:
function GetParameter<T extends RequestParameterType>(parameterName: string, parameters: Array<RequestParameter>): T | undefined
{
let result: T | undefined = undefined;
for (let parameter of parameters)
{
// Type check fails: 'T' only refers to a type, but is being used as a value here.
if (parameter.Name === parameterName && parameter.Value instanceof T )
{
// Possibly an issue here too:
// Type 'RequestParameterType' is not assignable to type 'T | undefined'.
// Type 'string' is not assignable to type 'T | undefined'.
result = parameter.Value;
}
}
return result;
}
我相信我可能需要编写类型保护函数,但我在编写类型保护时以同样的方式努力检查泛型类型。这有可能解决吗?
示例如下:in the Playground
TypeScript 编译为 JavaScript,这是实际得到的 运行。类型 T
及其规范如 <number>
或 <string>
将在编译时成为 erased,因此在 运行 时没有任何名为 T
的东西可以使用. instanceof
运算符专门仅在检查 class 构造函数时有效,并且由于您可能的 T
值主要是原始值,例如 string
和 boolean
,您不会无论如何我都不想使用 instanceof
("foo" instanceof String
是 false
)。
相反,您可能需要将 type guard function 作为参数传递给 GetParameter()
,因为这样的函数将在 运行 时存在。
也就是说,您可以将 GetParameter()
更改为
function GetParameter<T extends RequestParameterType>(
parameterName: string,
parameters: Array<RequestParameter>,
guard: (x: RequestParameterType) => x is T // new param
): T | undefined {
let result: T | undefined = undefined;
for (let parameter of parameters) {
// new check using guard() instead of instanceof
if (parameter.Name === parameterName && guard(parameter.Value)) {
result = parameter.Value; // no error
}
}
return result;
}
其中 guard()
必须是一个函数,它可以接受一些 RequestParameterType
的对象并将其缩小到 T
。这是一组你可以使用的:
const guards = {
number: (x: RequestParameterType): x is number => typeof x === "number",
string: (x: RequestParameterType): x is string => typeof x === "string",
boolean: (x: RequestParameterType): x is boolean => typeof x === "boolean",
// the only array matching RequestParameterType is number[], so we can
// just check to see if x is an array without needing to inspect elements
numberArray: (x: RequestParameterType): x is number[] => Array.isArray(x)
};
然后你可以这样调用 GetParameter()
:
let numberParam = GetParameter("one", parameters, guards.number);
console.log(numberParam); // 1
let stringParam = GetParameter("two", parameters, guards.string);
console.log(stringParam); // param2
let undefinedParam = GetParameter("two", parameters, guards.number);
console.log(undefinedParam); // undefined
请注意 guards.number
如何取代 <number>
。如果您检查 numberParam
的类型,它是 number | undefined
,返回值就是您所期望的。
好的,希望对您有所帮助;祝你好运!
我创建了一个联合类型:
type RequestParameterType = number | string | boolean | Array<number>;
我有一个 class,它是一个 key/value 对,持有该联合类型:
class RequestParameter
{
constructor(name: string, value: RequestParameterType)
{
this.Name = name;
this.Value = value;
}
public Name: string;
public Value: RequestParameterType;
}
然后我可以创建一个 RequestParameter 数组来保存 keys/values:
let parameters: Array<RequestParameter> = new Array<RequestParameter>();
parameters.push(new RequestParameter("one", 1));
parameters.push(new RequestParameter("two", "param2"));
我的想法是我可以编写一个 GetParameter
函数来从该数组中输入 return 类型的值,实际上我可能会这样使用:
// should return number type, with value 1
let numberParam: number | undefined = this.GetParameter<number>("one", parameters);
// should return string type, with value "param2"
let stringParam: string | undefined = this.GetParameter<string>("two", parameters);
// should return undefined, because param named 'two' is not number type
let undefinedParam: number | undefined = this.GetParameter<number>("two", parameters);
但是我的函数获取类型化参数时遇到问题,因为我不知道如何检查泛型类型是否与值类型匹配:
function GetParameter<T extends RequestParameterType>(parameterName: string, parameters: Array<RequestParameter>): T | undefined
{
let result: T | undefined = undefined;
for (let parameter of parameters)
{
// Type check fails: 'T' only refers to a type, but is being used as a value here.
if (parameter.Name === parameterName && parameter.Value instanceof T )
{
// Possibly an issue here too:
// Type 'RequestParameterType' is not assignable to type 'T | undefined'.
// Type 'string' is not assignable to type 'T | undefined'.
result = parameter.Value;
}
}
return result;
}
我相信我可能需要编写类型保护函数,但我在编写类型保护时以同样的方式努力检查泛型类型。这有可能解决吗?
示例如下:in the Playground
TypeScript 编译为 JavaScript,这是实际得到的 运行。类型 T
及其规范如 <number>
或 <string>
将在编译时成为 erased,因此在 运行 时没有任何名为 T
的东西可以使用. instanceof
运算符专门仅在检查 class 构造函数时有效,并且由于您可能的 T
值主要是原始值,例如 string
和 boolean
,您不会无论如何我都不想使用 instanceof
("foo" instanceof String
是 false
)。
相反,您可能需要将 type guard function 作为参数传递给 GetParameter()
,因为这样的函数将在 运行 时存在。
也就是说,您可以将 GetParameter()
更改为
function GetParameter<T extends RequestParameterType>(
parameterName: string,
parameters: Array<RequestParameter>,
guard: (x: RequestParameterType) => x is T // new param
): T | undefined {
let result: T | undefined = undefined;
for (let parameter of parameters) {
// new check using guard() instead of instanceof
if (parameter.Name === parameterName && guard(parameter.Value)) {
result = parameter.Value; // no error
}
}
return result;
}
其中 guard()
必须是一个函数,它可以接受一些 RequestParameterType
的对象并将其缩小到 T
。这是一组你可以使用的:
const guards = {
number: (x: RequestParameterType): x is number => typeof x === "number",
string: (x: RequestParameterType): x is string => typeof x === "string",
boolean: (x: RequestParameterType): x is boolean => typeof x === "boolean",
// the only array matching RequestParameterType is number[], so we can
// just check to see if x is an array without needing to inspect elements
numberArray: (x: RequestParameterType): x is number[] => Array.isArray(x)
};
然后你可以这样调用 GetParameter()
:
let numberParam = GetParameter("one", parameters, guards.number);
console.log(numberParam); // 1
let stringParam = GetParameter("two", parameters, guards.string);
console.log(stringParam); // param2
let undefinedParam = GetParameter("two", parameters, guards.number);
console.log(undefinedParam); // undefined
请注意 guards.number
如何取代 <number>
。如果您检查 numberParam
的类型,它是 number | undefined
,返回值就是您所期望的。
好的,希望对您有所帮助;祝你好运!