Search code examples
typescripttypestypescript-typingstypescript-generics

How to make Type that can only have keys from another Type but the keys can have a new value, new keys should throw an error


type Numbers = {
  a: number;
  b: number;
  f: number;
};

type SameKeysWithNeyTypes = SomeThingKeyOf<Numbers> & {
  a: string; 
  b: Date;
  c: null;   // Error here because c is not in Numbers ?
  // Error because f is missing
}

How can I do this? Basically the keys of the SameKeysWithNeyTypes type are validated from the keys in Numbers but with the possibility of changing their types one by one ?


Solution

  • You could define it as a generic utility type HasSameKeysAs<T, U> which always evaluates to U, but will complain unless it has exactly the same keys as T:

    type HasSameKeysAs<
      T, U extends {
        [P in keyof T | keyof U]: P extends keyof T ? any : never
      }> = U;
    

    This works because U is constrained to a type that depends on both T and U. We map over both the keys of T and the keys of U. Any key which is present in T will be required and allowed to be anything (with any) whereas any key which is present in U but not T will be forced to be the impossible never type, which essentially prohibits it (well, it allows never, but that's unlikely to accidentally show up... at least it didn't for your example use case).

    Let's try it:

    type SameKeysWithNeyTypes = HasSameKeysAs<Numbers, {
      a: string;
      b: Date;
    }> // error!
    // Property 'f' is missing in type '{ a: string; b: Date; }' 
    // but required in type '{ a: any; b: any; f: any; }'.
    
    type SameKeysWithNeyyTypes = HasSameKeysAs<Numbers, {
      a: string;
      b: Date;
      c: null;
      f: 1;
    }> // error!
    // Types of property 'c' are incompatible.
    
    type SameKeysWithNeyyyTypes = HasSameKeysAs<Numbers, {
      a: string;
      b: Date;
      f: 1;
    }> // okay
    

    Looks good. Missing keys and extra keys cause errors, while a type with exactly the same keys works fine.

    Playground link to code