Home > Software design >  How can I constrain property values to keys in the current type?
How can I constrain property values to keys in the current type?

Time:08-19

In typescript, I want to declare an extensible interface via an indexer, to allow additional functions to be associated dynamically, but have sub properties that refer back to those indexed functions by name. Something tells me this may be beyond the acrobatics the TS compiler is capable of, but I've rarely been able to stump it. Any ideas?

interface Foo {
  someProp: {
    [x: string]: keyof this; // doesn't work... but something like this to refer to indexed keys?
  };
  [x: string]: Function; // client can provide whatever functions they want in here
}

// usage
let myFoo: Foo = {
   bar: {
      validFuncName: "someFooFunc", // should work
      invalidFuncName: "thisAintNoFunc" // should error!
   },
   someFooFunc: () => {}
}

CodePudding user response:

Based on @kelly's answer, here is a slightly less "over engineered" solution.

function helper<T extends Record<string, any>>(obj: { 
   [K in keyof T]: K extends "bar" 
     ? Record<string, Exclude<keyof T, "bar">> 
     : Function 
}){}

helper({
   bar: {
      validFuncName: "someFooFunc", // ok
      validFuncName2: "someOtherFunc", // ok
      invalidFuncName: "thisAintNoFunc" // Error
   },
   someFooFunc: () => {},
   someOtherFunc: () => {}
})

Playground

CodePudding user response:

This is a variant of Tobias S.'s answer (and Kelly's original snippet) that maintains the type of the object passed to the helper function.

type ExtractFunctions<T> = {
    [K in keyof T as T[K] extends Function ? K : never]: T[K]
}

function helper<
    T extends { 
        [K in keyof T]: K extends "bar"
            ? Record<string, keyof ExtractFunctions<T>>
            : T[K]
    }
>(obj: T): T {
    return obj;
}

helper({
   bar: {
      validFuncName: "someFooFunc", // ok
      validFuncName2: "someOtherFunc", // ok
      invalidFuncName: "thisAintNoFunc" // Error
   },
   someFooFunc: () => {},
   someOtherFunc: () => {}
});
  • Related