TypeScript,详尽性检查无法正常工作

时间:2019-02-22 06:15:05

标签: typescript

比方说,我们正在为Post编写数据库模型,并且由于数据库将所有内容存储为字符串,因此我们需要编写parse函数,该函数将获取原始DB对象并将其转换为正确的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限制还是我错了?

2 个答案:

答案 0 :(得分:2)

首先,K扩展keyof Post并不意味着Kkeyof Post,因此必须使用{{1 }}以限制K的潜在值。

第二,如果您在tsconfig.json中设置keyof Post,则将强制使用默认选项。因此,将其设置为K即可解决问题。

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

希望获得帮助

答案 1 :(得分:1)

这种替代方法对您有吸引力吗?

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()
}