Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript: Type of a property dependent on another property within the same object

I have a TypeScript interface with two properties (type:string and args:object). The args may have different properties depending on the type. What type definition to I need to apply to args so the compiler/autocomplete will know which properties are allowed for args?

This is somewhat similar to how I use Actions in Redux, which do have a type and a payload and in my reducer the compiler knows by the switch-statement what the payload contains. But I can't get this to work with my object. I have read an excellent article here https://artsy.github.io/blog/2018/11/21/conditional-types-in-typescript/ but this describes the problem for a method with two args which depend on one another but not how to get this working for two properties within the same object.

export interface IObject {   type: ObjectType   parameters: ObjectParameters } 
export type ObjectType = "check" | "counter" 
export interface IParametersCheck {   checked: boolean } 
export interface IParametersCounter {   max: number   min: number   step: number } 
export type ObjectParameters = IParametersCheck | IParametersCounter 

If I have an IObject and set the type to "check" the compiler/autocomplete should offer the properties for IParametersCheck.

like image 980
smigfu Avatar asked Jul 09 '19 09:07

smigfu


1 Answers

I think what you are actually looking for is a discriminated union. IObject should itself be a union:

export type IObject = {     type: "checked"     parameters: IParametersCheck } | {     type: "counter"     parameters: IParametersCounter } export type ObjectType = IObject['type'] //// in case you need this union export type ObjectParameters = IObject['parameters']  //// in case you need this union  export interface IParametersCheck {     checked: boolean } export interface IParametersCounter {     max: number     min: number     step: number } 

You could also do it with conditional types but I think the union solution works better :

export interface IObject<T extends ObjectType> {     type: T     parameters: T extends 'checked' ? IParametersCheck: IParametersCounter } 

Or with a mapping interface:

export interface IObject<T extends ObjectType> {     type: T     parameters: ParameterMap[T] } type ParameterMap ={     'checked': IParametersCheck     'counter': IParametersCounter }  export type ObjectType = keyof ParameterMap 
like image 161
Titian Cernicova-Dragomir Avatar answered Sep 17 '22 16:09

Titian Cernicova-Dragomir