Home > Mobile >  Allow any input string when class is instantiated without a generic in TypeScript
Allow any input string when class is instantiated without a generic in TypeScript

Time:03-31

Consider the following class and types:

type keyCollection = {
  [key: string]: {
      request: any,
      response: any
  }
}

class myTest<TData extends keyCollection = any> {
  testFunction<
    TInput extends keyof TData, 
    TObject = TData extends keyCollection ? TData[TInput]['request'] : any>(input: TInput, data: TObject) {}
}

My goal is, that if this class is instantiated without any generic parameter, the testFunction should accept any string as it's first parameter, and anything as it's second parameter. Yet, when a generic is passed to the class, it should constrain the values of the testFunction.

Here are some test cases:

let a: myTest = new myTest();
a.testFunction('test', {}); // works as expected, no generic parameter for the class, thus accepts string as first parameter, and any as second


type testGenericKeyCollection = {
  a: {
    'request': string,
    'response': any
  },
  b: {
    'request': string,
    'response': any
  }
}
let b: myTest<testGenericKeyCollection> = new myTest();
b.testFunction("c", 25); // correctly errors out, as "c" as not a property of 
b.testFunction("a", 25); // why no error here? 25 is type number not type string - also, why no type helping here?

As you can see in the comments, the instantiation without generic seems to work in the first test case - the function accepts any string as it's first parameter, and anything as it's second parameter.

As for the second case, it still is ok, when the class is instantiated with a generic type provided, testFunction does not accept "c" as it's first parameter anymore, because it is not a property of the provided generic.

Yet, for the third test-case, while it correctly identifies the key from the input, it allows any type as the second parameter, and I can't figure out why. The third test-case should only accept a string type as it's second parameter.

Why is this not working the way I am expecting it to work?

Fiddle here

CodePudding user response:

You are using a default type (TObject = ...) instead of a type constraint:

class myTest<TData extends keyCollection = any> {
  testFunction<
    TInput extends keyof TData, 
    TObject extends TData extends keyCollection ? TData[TInput]['request'] : any>(input: TInput, data: TObject) {}
}

Playground Link

  • Related