【问题标题】:How to write a user defined type guard for "string" | "literal" | "types"?如何为“字符串”编写用户定义的类型保护 | “字面” | “类型”?
【发布时间】:2017-08-10 07:09:24
【问题描述】:

我有以下功能:

change(key: keyof McuParams, dispatch: Function) {
  /** Body removed for brevity */
}

当我调用函数时...

this.change(VARIABLE_FROM_MY_API, this.props.dispatch)

...我(可以理解)得到以下错误:

Argument of type 'string' is not assignable to parameter of type '"foo" | "bar"'

这是有道理的,因为编译器无法知道我的 API 在编译时发送了什么。但是,user defined type guards 有时可用于在运行时推断类型信息,并通过条件将该信息传递给编译器。

foo 仅定义为类型(而不是数组)时,是否可以为keyof 字符串类型(例如keyOf foo)编写用户定义的类型保护?如果有,怎么做?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    这是一个例子:

    interface McuParams {
        foo, bar, baz;
    }
    
    function change(key: keyof McuParams, dispatch: Function) {
    }
    
    function isKeyOfMcuParams(x: string): x is keyof McuParams {
        switch (x) {
            case 'foo':
            case 'bar':
            case 'baz':
                return true;
            default:
                return false;
        }
    }
    
    function doSomething() {
        const VAR_FROM_API = <string>'qua';
        if (!isKeyOfMcuParams(VAR_FROM_API)) return;
        change(VAR_FROM_API, () => { });
    }
    

    doSomething 中,您可以使用您喜欢的任何控制流块来代替return(例如ifthrow 等)。

    【讨论】:

    • 这会起作用,但问题是如果keyof McuParams 更改,类型保护可能不正确。它还需要重复,因为我需要保留一个在编译时使用的字符串列表和另一个将在运行时使用的列表。也许我试图在运行时获取编译时间信息超出了 TS 的范围?
    • 您可以随意实现isKeyOfMcuParams,例如如果有一种动态的方式来获取它,请使用它。如果关于哪些密钥有效的信息还没有显示出来,那么就没有办法了。
    【解决方案2】:

    尝试以下方法:

     enum mcuParams { foo, bar };
     type McuParams = keyof typeof mcuParams;    
    
     function isMcuParams(value: string) : value is McuParams {        
         return mcuParams.hasOwnProperty(value);        
     }
    
     function change(key: McuParams) {
         //do something
     }
    
     let someString = 'something';
    
    if (isMcuParams(someString)) {
        change(someString);
     }
    

    更新:

    我上面写的例子假设我们已经知道 McuParams 的可能值('foo' 或 'bar')。下面的例子不做任何假设。我测试了它,它按预期工作。根据随机生成的值,每次运行代码都会得到不同的响应。

    function getAllowedKeys() {
        //get keys from somewhere. here, I generated 2 random strings just for the sake of simplicity
        let randomString1 = String(Math.round(Math.random())); //0 or 1
        let randomString2 = String(Math.round(Math.random())); //0 or 1
        return Promise.resolve([randomString1, randomString2]);
    }
    
    function getKeyToBeTested() {   
        //same as in 'getAllowedKeys' 
        let randomString = String(Math.round(Math.random())); //0 or 1
        return Promise.resolve(randomString);        
    }
    
    Promise.all([getAllowedKeys(), getKeyToBeTested()]).then((results) => {    
        let allowedKeys: string[] = results[0];
        let keyTobeTested: string = results[1]; //'0' or '1'
        let mcuParams = {};
    
        for (let entry of results[0]) { //create dictionary dynamically     
            mcuParams[entry] = ''; //the value assigned is indiferent        
        }
    
        //create type dynamically. in this example, it could be '0', '1' or '0' | '1'
        type McuParams = keyof typeof mcuParams; 
    
        //create Type Guard based on allowedKeys fetched from somewhere
        function isMcuParams(value:string) : value is McuParams { 
            return mcuParams.hasOwnProperty(value);
        }
    
        function change(key: McuParams) { 
            //do something
            alert('change function executed: [' + allowedKeys.toString() + '] - ' + keyTobeTested);                
        }             
    
        if (isMcuParams(keyTobeTested)) {
            change(keyTobeTested);
        }
        else {
            alert('change function not executed: [' + allowedKeys.toString() + '] - ' + keyTobeTested);        
         }
    });
    

    【讨论】:

      【解决方案3】:

      更新:

      根据我的最新理解和您提供的背景信息,这是与您的用例相关的 sn-p 吗?如果是这样,它对我来说编译得很好。

      interface McuParams {
          foo: string;
          bar: string;
      };
      
      function change(key: keyof McuParams, dispatch: Function) {
          if (typeof key === 'foo') {
              console.log('call foo()');
          } else if (typeof key === 'bar') {
              console.log('call bar()');
          }
      }
      
      function callback(data) {
          change(data, this.props.dispatch)
      }
      

      【讨论】:

      • 这不是我想要的。上面的示例适用于像 'foobar' 这样的字符串文字(并且可以使用 keyof 运算符进一步简化),但它不适用于来自编译时未知的 API 的数据。
      • @Rick 你在说我的例子吗? 'foobar' 不会编译,但正如您所说,它可以在运行时工作。可能会问什么类型:McuParams?
      • 这是一个标准接口(对象)。 keyof 运算符将其转换为 "a" | "string" | "type"
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-08-09
      • 2013-11-27
      • 2021-08-10
      • 2020-03-17
      • 1970-01-01
      • 1970-01-01
      • 2019-01-03
      相关资源
      最近更新 更多