V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iqoo
V2EX  ›  程序员

TypeScript 能强制函数必须返回两种类型吗?

  •  
  •   iqoo · 2022-05-31 23:32:39 +08:00 · 2367 次点击
    这是一个创建于 667 天前的主题,其中的信息可能已经有所发展或是发生改变。

    比如 type X = string | number ,如果定义函数返回类型是 X ,那么只要是 string 或 number 其中一个就可以。

    如果想让函数两种类型都必须有返回(不同的分支返回不同的类型),是否有其他办法?

    第 1 条附言  ·  2022-06-01 10:12:46 +08:00

    大致是这样的功能:

    // 编译不通过
    function a() : string && number {
      return 123
    }
    
    // 编译不通过
    function b() : string && number {
      return 'hello'
    }
    
    // 编译通过
    function c() : string && number {
      if (...) {
        return 123
      }
      return 'hello'
    }
    
    21 条回复    2022-06-02 11:58:16 +08:00
    DOLLOR
        1
    DOLLOR  
       2022-05-31 23:51:05 +08:00
    没懂你想问什么,定义函数的时候返回值类型直接写“string | number”还不能达到你的需求么?
    thinkershare
        2
    thinkershare  
       2022-05-31 23:55:56 +08:00
    @DOLLOR 他想让编译器给他检查流程, 就是这个函数的返回必须必须即有 string 也有 number, 我目前是没想到有什么方法, 因为这个需求本质也是一种类型运算, 需要编译器检测函数返回类型, 然后组合. 但是目前 TS 似乎没有那个类型本身可用进行这种特殊的类型约束, 因为这个约束类型 never 这种, 需要超越类型本身的信息, 做分支流程检测.
    jsq2627
        3
    jsq2627  
       2022-06-01 00:05:05 +08:00
    ZE3kr
        4
    ZE3kr  
       2022-06-01 00:07:46 +08:00
    OP 想问的是如何保证这个函数有可能返回每一种类型

    但问题是根据定义,只返回 string 或 number 中的一种,这个函数的返回值类型也属于 X 。不如约定一个自己的类型 Y ( Y 是一个 Function/Class ),这样只要返回的是 Y 你就知道一定是都有返回了。

    Function/Method subtyping is always contravariant in its argument

    ProvideMore do(Take less);
    jsq2627
        5
    jsq2627  
       2022-06-01 00:21:08 +08:00
    https://www.typescriptlang.org/docs/handbook/2/conditional-types.html 这篇文档似乎是近期更新的,专门讲解 conditional type
    learningman
        6
    learningman  
       2022-06-01 00:48:00 +08:00 via Android
    你想要的是 overload
    Rocketer
        7
    Rocketer  
       2022-06-01 00:55:40 +08:00 via iPhone
    我还是看不懂需求,一个输入返回一个值,这个返回值不可能既是数字又是字符串。

    如果输入不同返回不同,那不是单元测试要做的事吗?
    Opportunity
        8
    Opportunity  
       2022-06-01 01:01:34 +08:00   ❤️ 1
    你需要 tsd ,实例里就有你说的内容

    https://github.com/SamVerschueren/tsd#strict-type-assertions
    liuzhaowei55
        9
    liuzhaowei55  
       2022-06-01 01:01:58 +08:00 via iPhone
    题主可以自己拆解这个函数为两个子函数分别处理两个分支,然后在外部包装一层,判断入参应该走哪个分支,即拆分了逻辑又对响应数据类型做了保证。
    iosyyy
        10
    iosyyy  
       2022-06-01 01:17:05 +08:00
    楼主的意思是强制要求返回 number+string 吧上面感觉有点跑题
    我个人觉得还是分成两个函数解决好点 你可以做一个 map 映射一下然后分别调用
    vision1900
        11
    vision1900  
       2022-06-01 01:30:29 +08:00
    函数本身只是个过程,它在执行前不知道究竟哪些分支会被执行。如果一个函数即可能返回 string 也可能返回 number ,而且具体返回 string 还是 number 的判断是在函数内,那么他就只能返回 string | number.

    这个问题本身无解。

    假设原来的 function 是这样:

    ```typescript
    // 获取日本人的出生年(尽可能用国号纪年)
    function getBirthYear(age: number) {
    const year = (new Date()).getFullYear() - age
    if (year > 2019) {
    return `令和${year - 2019}年`
    } else if (year > 1989) {
    return `平成${year - 1989}年`
    } else if (year > 1926) {
    return `昭和${year - 1926}年`
    } else {
    return year
    }
    }

    console.log(getBirthYear(100)) // 1922
    console.log(getBirthYear(24)) // "平成 9 年"
    ```
    muzuiget
        12
    muzuiget  
       2022-06-01 01:33:03 +08:00
    估计要的是泛型,也就是尖括号用法

    getStringOrNumber<string>('aString');
    getStringOrNumber<number>(1);
    shakukansp
        13
    shakukansp  
       2022-06-01 01:40:35 +08:00
    overload
    renhou
        14
    renhou  
       2022-06-01 07:50:05 +08:00
    可以的,函数的重载
    lneoi
        15
    lneoi  
       2022-06-01 09:15:10 +08:00
    听着像是重载,一种输入对应一种返回类型
    raykle
        16
    raykle  
       2022-06-01 09:48:54 +08:00
    OP 需求没描述清楚。

    @muzuiget 乍一看我也觉得是泛型

    type X = string | number
    getStringOrNumber<X>(params: X): X
    zingwu
        17
    zingwu  
       2022-06-01 10:08:25 +08:00
    重载吧
    zhuweiyou
        18
    zhuweiyou  
       2022-06-01 12:04:34 +08:00
    泛型
    jeffhong
        19
    jeffhong  
       2022-06-01 16:06:35 +08:00 via iPhone
    你需要 sum type ,大多数编程语言不支持。不过你可以用 product type 来模拟下。
    qbqbqbqb
        20
    qbqbqbqb  
       2022-06-02 11:35:41 +08:00
    @raykle OP 需求应该是想对 union type 返回值做反向的 exhaustiveness check.

    正向的 exhaustiveness check 主要是针对这种值的读取的,比如说有些语言的 switch-case 或模式匹配如果缺了几个 case 又没有显式写 default case 的话编译器就会产生 warning 甚至 error 这样。一般不针对赋值、返回值等写入操作。

    而 OP 希望对函数返回值的写入也可以进行这种 check 。就比如说你有一个类型
    type X = string | number
    然后定义一个函数
    getStringOrNumber(): X
    这时如果函数体内只有 return string 的语句而没有 return number 的语句, [也就是说这个 X 类型里有几种情况没有用到,可以改成 getStringOrNumber(): string 也不出错] ,OP 希望遇到这种情况编译器可以提供 warning 或者 error 。
    qbqbqbqb
        21
    qbqbqbqb  
       2022-06-02 11:58:16 +08:00
    这个需求在 TypeScript 里其实不好做。因为 ts 里有一种 literal type 的东西。比如你的例子里的 c 函数,如果不手动指定返回类型的话,你以为编译器推断出的类型是 string|number ,结果其实是"hello"|123 ,也是一种比 string|number 更窄的类型,那它该不该报错呢? 按照你的需求应该是不报错,但是按照 ts 的类型系统"hello"|123 和 number 都是 extends string|number ,并不好区分。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2796 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 13:32 · PVG 21:32 · LAX 06:32 · JFK 09:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.