Home > Enterprise >  Infer object types by sibling property - Typescript
Infer object types by sibling property - Typescript

Time:01-18

Is it possible to assert properties of an object based on another property within that same object?

But how can I infer the types for values based on the value of type property?

type StepKeys = "Test1" | "Test2";

interface objectMap {
    "Test1": {
        name: string
    },
    "Test2": {
        age: number
    }
};

interface Step<T extends StepKeys = StepKeys> {
    type: T;
    value: objectMap[T]
};

const steps: Record<string, Step> = {
    "Step1": {
        type: "Test1",
        value: {

        }
    }
}

Here types for values are a union of { name: string; } | { age: number; }.

Is it possible to infer it's possible values?

Typescript

CodePudding user response:

Hi should you want to effectively discriminate this union, the type parameter should not be a union, instead seek to "push" the union one type up. This is because we want each Step type to have it's own unique type parameter, instead of the union of possible type parameters. So a small change, but accomplishes what you want.

const steps: Record<string, Step<'Test1'> | Step<'Test2'>> = {
    "Step1": {
        type: "Test1",
        value: {
            // infer ?
            age: 11,
// ^^^ correctly throws error 
// Object literal may only specify known properties, and 'age' does not exist in type '{ name: string; }'.(2322)
            name: 'test',
        }
    }
}

Using an indexed access map type you can create a discriminated union automatically if there are a lot more key/value pairs.

type Steps = Record<string, {
    [key in StepKeys]: Step<key>
}[StepKeys]>

View on TS Playground

CodePudding user response:

The only way to achieve what you want isn't really good but it works. The problem is that you have to manually enter every key (which makes it impossible to make it generic or scalable).

interface PeoplePropsMap {
    withName: {
        name: string
    },

    withAge: {
        age: number
    }
};

type People =
    { type: 'withName', value: PeoplePropsMap['withName'] } |
    { type: 'withAge',  value: PeoplePropsMap['withAge']  }

const steps: Record<string, People> = {
    john: {
        type: 'withAge',
        value: {
            age: 10  
        }
    }
}
  • Related