Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatically initialize member field for string literal types in Typescript

Tags:

typescript

If I have the classes

class Foo {
  type: "foo"
  constructor(public id: number) {}
}

class Bar {
  type: "bar"
  constructor(public id: number) {}
}

type SomeUnion = Foo | Bar // (x : SomeUnion).type will have the type "foo" | "bar"

Constructing an instance of new Foo(1) will yield { id: 1 } at runtime, but I would like to get { id: 1, type: "foo" }.

A simple solution is of course to type the string constant twice, by assigning it in the constructor for example with this.type = "class_name".

However, this is non-ideal since I need to type the same string constant twice. It's not a huge deal, but it's the kind of thing one could forget and lead to bugs.

Is there a way to auto-create a member of the object with the same value as the string literal?

like image 638
Advecticity Avatar asked Mar 10 '23 04:03

Advecticity


2 Answers

Let's concentrate on this code:

class Foo {
  type: "foo"
  constructor(public id: number) {}
}

If the compiler option strictNullChecks is false, then type: "foo" implicitly also allows setting type to the values undefined and null. I hope the need to explicitly initialize the field to a value other than undefined is obvious.


So let's move on with strictNullChecks set to true. In this case, the only value that can be assigned to type is "foo". It looks like what you're hoping to have some way to get the compiler to perform the following reasoning:

type: "foo" says the field named type must be of type "foo". Therefore, only the value "foo" can be assigned to it. Consequently, I should emit code to automatically initialize it to "foo" because there's no other value that can be assigned to it.

The problem is that TypeScript 2.x even with strictNullChecks currently allows fields to start with the value undefined even if assigning undefined is not valid. Let me put this explicitly: even though you cannot assign the value undefined to type, the field type can start with the value undefined (if you do not initialize it). This is not deemed an error. This is how TypeScript is currently designed. There is an issue report about this, with some people arguing it is a design flaw in the language.

At any rate, the compiler does not perform the desired reasoning above. For now, one way or another, you will have to repeat the same value as part of the string literal type and as part of the initialization of your type fields.

like image 59
Louis Avatar answered Mar 13 '23 12:03

Louis


I think this will do what you are looking to do. You still have to type out the string literal twice, but they are typed out in close proximity, so you would be less likely to forget if the class type ever changes to update both values.

class Foo {
  type: "foo" = "foo"
  constructor(public id: number) {}
}

class Bar {
  type: "bar" = "bar"
  constructor(public id: number) {}
}
like image 41
TJ Rockefeller Avatar answered Mar 13 '23 14:03

TJ Rockefeller