通用联合体类型的Typeguard

时间:2019-09-12 12:59:59

标签: typescript typeguards

我创建了一个联合类型:

type RequestParameterType = number | string | boolean | Array<number>;

我有一个类,它是一个拥有联合类型的键/值对:

class RequestParameter
{
    constructor(name: string, value: RequestParameterType)
    {
        this.Name = name;
        this.Value = value;
    }

    public Name: string;
    public Value: RequestParameterType;
}

然后我可以使该RequestParameter的数组保留键/值:

let parameters: Array<RequestParameter> = new Array<RequestParameter>();
parameters.push(new RequestParameter("one", 1));
parameters.push(new RequestParameter("two", "param2"));

我的想法是我可以编写一个GetParameter函数以从该数组返回类型化的值,在实践中我可能会这样使用:

// 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;
}

我相信我可能需要编写一个typeguard函数,但是在编写typeguard时我正以同样的方式来检查泛型。这将有可能解决吗?

这里是示例:in the Playground

1 个答案:

答案 0 :(得分:1)

TypeScript编译为实际运行的JavaScript。类型expect(getByText('Click Me').closest('a')).toHaveAttribute('href', 'https://www.test.com/') 及其规格,例如T<number>在编译时将是erased,因此在运行时没有什么可使用的<string>T运算符仅在检查类构造函数时才有效,并且由于可能的instanceof值主要是诸如Tstring之类的基元,因此您不想使用无论如何booleaninstanceof"foo" instanceof String)。

相反,您可能需要将type guard function传递给false作为参数,因为这样的函数将在运行时存在。

也就是说,您可以将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,则返回的值就是您期望的值。

好的,希望能有所帮助;祝你好运!

Link to code