1

In Typescript (5.0+), how can I achieve the following:

export interface MyFirstInterface {
  fieldCanOnlyBeString: string,
  fieldCanOnlyBeNumber: number,
  [unknownFieldName:string]: MyCustomInterface
}

Whenever I declare a variable with the MyFirstInterface type, I would like one field to be a number, one is a string and the rest be MyCustomInterface

How can I declare this in TS, given the classic [key:string]: string would mean every string key-type must be a string type?

What is the correct way to realise the idea of "specific field names have a specific type, all the other fields must be this other type" in Typescript?

I have tried with a couple of generics with various types, unions, never's etc... but no other solutions really answered my problem in straightforward manner.

timeimp
  • 11
  • 3
  • Unfortunately there's no great solution here, only various workarounds. See the linked q/a for more information. – jcalz Jul 06 '23 at 12:34

1 Answers1

0

You can use an intersection :

declare type MyCustomInterface = {};

export type MyType = {
  fieldCanOnlyBeString: string,
  fieldCanOnlyBeNumber: number,
} & {[unknownFieldName:string]: MyCustomInterface}


const foo:MyType = {fieldCanOnlyBeNumber:3, fieldCanOnlyBeString: 'test', foo: 'im good'}

Playground

Matthieu Riegler
  • 31,918
  • 20
  • 95
  • 134
  • And why is this intersection non-empty? To me, intersection of "all keys are of type X and values are of type Y" with "key K has value of type Z which is not a subtype of Y" should be strictly null set. Is it one more place where typescript invents is own typing theory that does not match the math behind? – STerliakov Jul 06 '23 at 09:49
  • @SUTerliakov-supportsstrike that's because `string & {}` is not `never` (unlike e.g. `string & number`) – Dimava Jul 06 '23 at 10:44
  • @Dimava ough. Because `{}` is a subtype of string here. So this will die for non-blank interface, right? – STerliakov Jul 06 '23 at 10:47
  • Oh, yes, it does fail for (almost?) all interfaces but a trivial one. – STerliakov Jul 06 '23 at 10:50
  • @SUTerliakov-supportsstrike for non-partial ones unless you typecast – Dimava Jul 06 '23 at 12:29
  • Empty object types are not good examples; I'd always recommend adding some structure even for toy code, otherwise you will accidentally recommend approaches that don't actually work. Intersections like this work fairly well for *consuming* a value of the desired type, but are not good for *producing* such a value. – jcalz Jul 06 '23 at 12:38