Home > Software engineering >  TypeScript: Add custom type to a single property in object
TypeScript: Add custom type to a single property in object

Time:09-12

Let's say there is an object that looks like this:

const object = {
  prop: "prop",
  typedProp: "type-1",
};

I want the typedProp property to only accept the following values: "type-1", "type-2", "type-3". To do this, I created a custom type that looks like this:

type CustomProp = "type-1" | "type-2" | "type-3";

I know that I can assign this type like so:

const object: {
  prop: string;
  typedProp: CustomProp; // <-- Adding type here
} = {
  prop: "prop",
  typedProp: "type-1",
};

My question is, is there a way to assign this type directly to typedProp (within the object) so that I can avoid adding the type to the whole object? I tried to implement it this way:

const object = {
  prop: "prop",
  typedProp: "type-1" as CustomProp,
};

but it doesn't work as expected, because with this approach, I can add any string to typedProp, which is not what I would like to achieve. Could you please advise if there is a way to do this?

CodePudding user response:

You're looking for a "safe upcast" operator, or a "widening-only type assertion". TypeScript doesn't have direct support for such a thing. There's long been discussion of (and possibly soon there will be an implementation of) the so-called satisfies operator, the subject of microsoft/TypeScript#7481 and now microsoft/TypeScript#47920, which some people want for reasons like this, although it looks like the "safe upcast" part won't be supported. So for the foreseeable future there's not going to be syntax that does this for you.

Luckily you can emulate this with a helper function.

The function const customProp = (t: CustomProp) => t will only accept a CustomProp input, and will return its input with the type CustomProp. There won't be any widening to string or narrowing to just the single "type-1" string literal type:

const customProp: (t: CustomProp) => t;

const badObject = {
  prop: "prop",
  typedPop: customProp("oopsie-doodle") // error! 
}

const object = {
  prop: "prop",
  typedProp: customProp("type-1"), // okay
};

object.typedProp = "type-2"; // okay

If you find yourself doing this sort of thing often, you can write a generic version of this function that accepts a value of manually specified type T and returns that value with a non-widened non-narrowed type T:

const safeUpcast = <T,>(t: T) => t;

And then the customProp function is just a special case of safeUpcast instantiated with T being CustomProp:

const customProp = safeUpcast<CustomProp>;

Playground link to code

CodePudding user response:

Maybe you can try using TS "Enums",

Define your custom prop as follows,

enum CustomProp {
  type1 = "type-1",
  type2 = "type-2",
  type3 = "type-3"
}

And then use it as follows,

const object = {
  prop: "prop",
  typedProp: CustomProp.type1,
};

W3Schools Guide: https://www.w3schools.com/typescript/typescript_enums.php

  • Related