typescripttypescript-generics

How to convince typescript that a record has a value?


If I have this declaration:

type MyAwesomeLookup = Record<string, number>

How can I then write a hasNumber type guard that makes this compile without errors:

const lookup = { [Math.random().toString()]: 42 }
const key = '0.5'

if (hasNumber(lookup, key)) {
  const x: number = lookup[key]
  console.log({ x })
}

I am using TypeScript 5.x.


Solution

  • You could use an intersection to ensure the lookup contains a key:

    Playground

    type MyAwesomeLookup = Record<string, number>
    
    const lookup = { [Math.random().toString()]: 42 }
    const key = '0.5'
    
    const hasNumber = <K extends string>(lookup: MyAwesomeLookup, key: K): lookup is MyAwesomeLookup & {[P in K]: number } => key in lookup;
    
    if (hasNumber(lookup, key)) {
      const x = lookup[key]
      console.log({ x })
    }
    

    Though I would suggest to narrow directly:

    Playground

    type MyAwesomeLookup = Record<string, number>
    
    const lookup = { [Math.random().toString()]: 42 }
    const key = '0.5'
    
    if (key in lookup) {
      const x = lookup[key]
      console.log({ x })
    }