typescripttypescript-generics

Poor inference during instantiation of the class


The goal of this code is to extend the functionalities of the t_Prisma object through the Prisma class.


type t_Prisma<MNs extends Record<string, string> > = {
    ModelName: MNs
}


class Prisma1<P extends t_Prisma<MNs>, MNs extends Record<string, string>> implements t_Prisma<MNs> {
    _prisma: P;

    constructor(_prisma: P) {
        this._prisma = _prisma;
    }
    get ModelName() {
    return this._prisma.ModelName;
    }
}


class Prisma2<MNs extends Record<string, string>> implements t_Prisma<MNs> {
    _prisma: t_Prisma<MNs>;

    constructor(_prisma: t_Prisma<MNs>) {
        this._prisma = _prisma;
    }

    get ModelName() {
        return this._prisma.ModelName;
    }
}


const _Prisma = {
    ModelName:{
        Model1: "Model1",
        Model2: "Model2",
        Model3: "model3"
    } as const 
}

const prismaInstance1 = new Prisma1(_Prisma)
type test_ModelName1 = typeof prismaInstance1.ModelName

const prismaInstance2 = new Prisma2(_Prisma)
type test_ModelName2 = typeof prismaInstance2.ModelName

In the first code (Prisma1), the inference for ModelName doesn't work as expected (the inferred type is Record<string, string>), but it feels more natural.

In the second code (Prisma2), the inference works correctly ({Model1: "Model1", ...}), but it forces me to repeat the type t_Prisma<MNs> everywhere in the class definition , which is not ideal.

I really want to use the first pattern because it exactly represents what I want to do: parameterize a class with an object of type t_Prisma, but I'm not sure if it's a good practice since it doesn't work that way.

EDIT: constructor(_prisma: P & t_Prisma<MNs>) seems to work


Solution

  • You can use typescript constructor notation to lessen the repetition:

    Playground

    type t_Prisma<MNs extends Record<string, string> > = {
        ModelName: MNs
    }
    
    class Prisma1<MNs extends Record<string, string>> implements t_Prisma<MNs> {
    
        constructor(public _prisma: t_Prisma<MNs>) {
            
        }
    
        get ModelName() {
          return this._prisma.ModelName;
        }
    }
    
    const _Prisma = {
        ModelName:{
            Model1: "Model1",
            Model2: "Model2",
            Model3: "model3"
        } as const 
    }
    
    const prismaInstance1 = new Prisma1(_Prisma)
    type test_ModelName1 = typeof prismaInstance1.ModelName