Home > Enterprise >  TypeScript: "extends" string literal type check not working
TypeScript: "extends" string literal type check not working

Time:12-22

When trying to type check the dependency between keys and values in an object, I try to define MyObject as:

type Key =  AKey| BKey
type AKey = `A${string}`
type BKey = `B${string}`
type Item<Key> = 
   Key extends AKey ? {a:string} : 
   Key extends BKey ? {b:number} : 
   {}
type MyObject = {[key:Key]:Item<Key>}

There is no error, however invalid inputs are accepted without errors:

let test1:MyObject = {"Afoo":{a:"yes"}}
let test2:MyObject = {"Afoo":{b:1}} // should fail
let test3:MyObject = {"Bbar":{a:"no"}} // should fail
let test4:MyObject = {"Bbar":{b:2}}

It looks like extends is not working with string literal types or is it something else?

I know about as const for a closed set of keys but this doesn't apply here as I need the keys set to be infinite.

CodePudding user response:

As it written now MyObject results in:

{
    [key: `A${string}`]: { a: string } |  { b: number };
    [key: `B${string}`]: { a: string } |  { b: number };
}

That's because union AKey| BKey is passed to Item<Key>. But we want to resolve the value for each key separately. This can be achieved using mapped type:

type MyObject = { [K in Key]: Item<K> }

let test1: MyObject = { "Afoo": { a: "yes" } }
let test2: MyObject = { "Afoo": { b: 1 } } // now fails
let test3: MyObject = { "Bbar": { a: "no" } } // now fails
let test4: MyObject = { "Bbar": { b: 2 } }

Now MyObject is resolved to:

{
    [key: `A${string}`]: { a: string };
    [key: `B${string}`]: { b: string };
}

Playground

  • Related