Typescript one of interface

Creating typescript type «one of interfaces»

A union of two interfaces A and B is a type for all objects which are A or B. To work with such type reliably, the compiler has to be sure that only keys that are present in both A and B are used. In other words, a union of objects contains only an intersection of their keys.

Creating typescript type «one of interfaces»

I need to create type which it’s behaviour would be choosing between one of given interfaces.

Please note: in the example below someTask is data returned from the server! There is no actual initiation of it in the code! For example:

interface TaskA < foo: string >interface TaskB < fee: string >type Task = TaskB | TaskB const task:Task = someTask console.log(task.fee) 

The console.log function will yell at me:

Property ‘fee’ does not exist on type ‘Task’.

What am I doing wrong here?

The terms «union» and «intersection» as used in Typescript are mathematically correct, but still a bit confusing when applied to objects/interfaces. A union of two interfaces A and B is a type for all objects which are A or B. To work with such type reliably, the compiler has to be sure that only keys that are present in both A and B are used. In other words, a union of objects contains only an intersection of their keys. Conversely, an intersection of A and B, A & B , contains all keys from both A and B, that is, an intersection of interfaces is a union of their keys:

type A = < a: 1, b: 2, x: 3 >type B = < a: 1, b: 2, y: 3 >type U = keyof (A | B) // a|b type I = keyof (A & B) // a|b|x|y 

Since your objects don’t have any common keys, their union is essentially an empty object ( ). It cannot have any properties.

Читайте также:  Usla ru students eadzd php

What you can do, is to introduce a tagged union, in which all member types have a common property («tag») which enables the compiler to differentiate between them. Example:

type SomeTask> = < type: T >& P type TaskOne = SomeTask> type TaskTwo = SomeTask> type Task = TaskOne | TaskTwo; function validateTask(t: Task)

Here, the compiler knows, that if the type is first , then the object is expected to have the one property.

Make sure that you have defined property ‘fee’ in your object.

If you can’t be sure that the object you get from your server is a Task, you should add a User Defined Type Guard for either TaskA or TaskB. That way you can be sure that someTask has fee as a property, in this case.

interface TaskA < foo: string >interface TaskB < fee: string >type Task = TaskA | TaskB const task: Task = ; console.log(isTaskA(task) ? task.foo : task.fee) function isTaskA(value: TaskA | TaskB): value is TaskA

TypeScript String, In TypeScript, the string is an object which represents the sequence of character values. It is a primitive data type which is used to store text data. The string values are surrounded by single quotation mark or double quotation mark. An array of characters works the same as a string.

Create type as values of list typescript

typescript one of array

function stringLiterals(. args: T[]): T[] < return args; >type ElementType> = T extends ReadonlyArray ? ElementType : never; const values = stringLiterals('A', 'B'); type Foo = ElementType; const v1: Foo = 'A' // This should work const v2: Foo = 'D' // This should give me an error since 'D' doesn't exist in values

typescript list

enum Color < Red = "red", Green = 2, Blue = 4, >let c: Color = Color.Green;Try

Javascript — Typescript interface property to string, If you need to validate the strings you can create a new type based on keyof from the interface. If you have an object you can use keyof typeof object. Example for language files: localizationService.ts

Programatically create a typed Record / Object from the keys of another one

In my typescript program (code below) I define two base types ( Player , State ) and then a few nested Record types used as mappings.

I then in a typed function create an instance of one of these records based on an existing instance of the nested record.

 type Player = "1" | "2"; type State = "A" | "B" | "C"; type StateMapping = Record; type PlayerStateMappings = Record type PlayerStates = Record; const playerStateMappings: PlayerStateMappings = < "1": < "A": "B", "B": "C", "C": "A" >, "2": < "C": "B", "B": "A", "A": "C" >, > function nextStates(currentState: State): PlayerStates < var nextStates = <>; for(const player of Object.keys(playerStateMappings)) < nextStates[player] = playerStateMappings[player][currentState] >return nextStates; > console.log(nextStates("A")) 

This code throws the following type error at the return statement, since I created the object without the required keys and only added those afterwards: TS2739: Type ‘<>‘ is missing the following properties from type ‘PlayerStates’: 1, 2 .

My question is if there is a way to avoid this type error that fulfils the following requirements :

  1. The typesystem is not relaxed in particular it still enforces that the nextStates function returns a complete and valid PlayerStates object.
  2. The nextStates object is created programatically based on the keys of the playerStatesMapping object, meaning I don’t have to hardcode all of the players again.

After some research on SO I found a few options that avoid the error but all of which violate one of the two requirements mentioned above:

Approaches that violate condition 1:

  1. Make the PlayerStates type partial : type PlayerStates = Partial>;
  2. Enforce type using as keyword: var nextStates = <> as PlayerStates ; (from this question)

Approaches that violate condition 2:

I know that the whole typing is a bit of an overkill in the above example but this is a highly simplified / reduced version of the problem that I encountered in a more complex project where above requirements / expectations make more sense.

PS: coming from a Python background I guess I am looking for something like a dict comprehension that allows me to initialize a new dictionary based on some iteration.

Based off @hackape’s answer I was able to produce the following, but it required redefining Object.fromEntries and Object.entries :

type Player = "1" | "2"; type State = "A" | "B" | "C"; type StateMapping = Record; type PlayerStateMappings = Record type PlayerStates = Record; const playerStateMappings: PlayerStateMappings = < "1": < "A": "B", "B": "C", "C": "A" >, "2": < "C": "B", "B": "A", "A": "C" >, > // stricter version of Object.entries const entries: >(obj: T) => Array = Object.entries // stricter version of Object.fromEntries const fromEntries: (entries: Iterable) => Record = Object.fromEntries function nextStates(currentState: State): PlayerStates < return fromEntries( entries(playerStateMappings).map(([player, mapping]) =>[player, mapping[currentState]] ) ) > 

Try the functional programming way. You can avoid using the nextStates variable altogether. Problem gone.

function nextStates(currentState: State): PlayerStates < return Object.fromEntries( Object.entries(playerStateMappings).map(([player as Player, mapping]) =>[player, mapping[currentState]] ) ) > 

The type error is from your nextStates variable as you haven’t specified it’s type so it’s inferred as <> when you define it as the empty object.

You can use a Partial type only for your nextStates variable to allow it to start off empty, but you will need a way to tell the compiler when the full PlayerStates object has been created using a guard. Here’s an example:

type Player = "1" | "2"; type State = "A" | "B" | "C"; type StateMapping = Record; type PlayerStateMappings = Record type PlayerStates = Record; const playerStateMappings: PlayerStateMappings = < "1": < "A": "B", "B": "C", "C": "A" >, "2": < "C": "B", "B": "A", "A": "C" >, > // Stricter version of keys which returns Array instead of string[] const keys = >(obj: T): Array => Object.keys(obj); // Type guard to convert Partial to PlayerStates const isCompletePlayerStatesObj = ( obj: Partial ): obj is PlayerStates => obj["1"] !== undefined && obj["2"] !== undefined; function nextStates(currentState: State): PlayerStates < var nextStates: Partial= <>; for(const player of keys(playerStateMappings)) < nextStates[player] = playerStateMappings[player][currentState] >if (!isCompletePlayerStatesObj(nextStates)) < throw new Error("Whoops this function was badly implemented") >return nextStates; > console.log(nextStates("A")); 

JavaScript String (with Examples), Create JavaScript Strings. In JavaScript, The String() function is used to convert various data types to strings. For example, const a = 225; // number const b = true; Try Programiz PRO: Learn to code interactively with step-by-step guidance. Try For Free

Источник

Оцените статью