Search code examples
typescriptextends

Typescript conditional extends with nested object


I am building a type that checks if a member of a class-type is an object and if so, a specific type must be assigned. I provide a more explanatory example below.

Here we have the class example we are going to use.

declare class myObject {
    name?: string
    id?: number
    parent?: {
        child: number
    }
}

Here we have the type i am trying to make

type myType<T> = {
    [K in keyof T]: T[K] extends {[k:string]:any} ? T[K] : "something"
}

It should check that parent member is in fact an object of that type and allow something like this

const example: myType<myObject> = {
    parent: {
        child: 0
    },
    name: "something"
    id: "something"

But it doesn't because parent? doesn't get checked successfully against {[k:string]:any}. This happens because parent? is an optional member. If i change parent? to parent it works as expected. To try and work around this I tried switching {[k:string]:any} with {[Z in keyof T[K]]?: T[K][Z]} but i observed the same exact behaviour.

A link to TS Playground


Solution

  • You are right when you say this happens because parent? is an optional member.

    But what does it mean to be an optional member?

    In JS it means the property can either have a value or be undefined.

    In TS, it means that the type of parent? is actually something like: {[k:string]: any} | undefined

    The code below solves the error on the linked TS playground.

    type myType<T> = {
        [K in keyof T]?: T[K] extends ({[k:string]: any} | undefined) ? T[K] : "something"
    }