【问题标题】:TypeScript, exhaustiveness check not working properlyTypeScript,详尽性检查无法正常工作
【发布时间】:2019-02-22 17:51:23
【问题描述】:

假设我们正在为Post 编写数据库模型,因为数据库将所有内容都存储为字符串,我们需要编写parse 函数,该函数将获取原始数据库对象并将其转换为正确的Post 接口。

重现设置noImplicitReturns: true

interface Post {
  id:   number
  text: string
}

function parse<K extends keyof Post>(k: K, v: any): Post[K] {
  switch(k) {
    case 'id':   return parseInt(v)
    case 'text': return v.toString()
  }
}

该代码中有两个错误。首先 - 它不会编译,似乎 TypeScript 没有意识到我们的代码是正确的,并要求将 default 语句添加到 switch 中。

第二个错误 - 它不会检测到您正在检查错误的值。下面的错误代码编译不会出错

function parse<K extends keyof Post>(k: K, v: any): Post[K] {
  switch(k) {
    case 'id':   return parseInt(v)
    case 'text': return v.toString()
    case 'some': return v.toString() // <= error, no `some` key
    default: return ''               // <= this line is not needed
  }
}

还有第三个错误,TypeScript 允许返回错误的键值,这个错误的代码会编译

function parse<K extends keyof Post>(k: K, v: any): Post[K] {
  switch(k) {
    case 'id':   return parseInt(v)
    case 'text': return 2 // <= error, it should be string, not number
    default: return ''
  }
}

是那些 TypeScript 限制还是我做错了什么?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    首先,K 扩展keyof Post 并不意味着Kkeyof Post,因此您必须使用keyof Post 来限制K 的潜在值,而不是使用泛型类型K

    其次,如果您在 tsconfig.json 中设置 "switch-default": true,,则强制使用默认选项。所以将它设置为false 然后问题就会消失。

    第三,Post[K] 甚至Post[keyof Post] 将返回Post 属性的所有可能类型,因此number 是可能的。 Typescript 不会强制 Post[K] 为其 K 属性的类型,除非您指出哪个 K 是。否则,您必须定义映射类型,如类型A = ['id', numer] | [text', string] 并将Post 定义为[A[0]]: A[1]

    希望有帮助

    【讨论】:

    • 谢谢回复,能不能也附上parse函数的固定版本?我正在尝试实现这部分 so instead of using generic type K, you must use keyof Post to limit the potential values of K 但它会抱怨。
    【解决方案2】:

    这个替代方案会吸引你吗?

    interface Post {
      id:   number
      text: string
    }
    
    type Parsers = {
        [k in keyof Post]: (v: any) => Post[k]
    }
    
    const parsers: Parsers = {
        id (v: any) {
            return parseInt(v)
        },
        text (v: any) {
            return v.toString(v)
        }
    }
    
    parsers.id('123') // number
    parsers.text({}) // string
    
    // In addition, adding extra parsers or returning a value of wrong type from a parser is type-checked.
    

    更新

    使Parser(renamed from Parsers)类型声明泛型并利用箭头函数和上下文类型推断,代码可以改进为:

    type Parser<T> = {
        [k in keyof T]: (v: any) => T[k]
    }
    
    interface Post {
      id:   number
      text: string
    }
    
    const parser: Parser<Post> = {
        id: v => parseInt(v),
        text: v => v.toString()
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多