Home > Blockchain >  How to allow Typescript Generic parameter to have default
How to allow Typescript Generic parameter to have default

Time:07-05

I am trying to set a default for a generic function in TS. Here is a simplistic example:

type Size = "small" | "medium" | "large";
type Detail<S extends Size> = S extends "small" ? "noDetail" : S extends "medium" ? "partialDetail" : "fullDetail"

function getDetail<S extends Size>(size:S = "small"):Detail<S>{
    if(size === "small")
        return "noDetail" as Detail<S>;
    if(size === "medium")
        return "partialDetail" as Detail<S>
    return "fullDetail" as Detail<S>;
}

It causes the error:

Type '"small"' is not assignable to type 'S'.
  '"small"' is assignable to the constraint of type 'S', but 'S' could be instantiated with a different subtype of constraint 'Size'.ts(2322)

I understand the problem (ex: someone could try getDetail<"large">(); ) and I've read several posts on SO to try and resolve.

However,

  1. I don't want to force people to pass a parameter.
  2. I want to return the conditional type (not unconstrained string, not union)

How can I do something like this?

CodePudding user response:

This is better done with function overloads

function getDetail(): Detail<"small">;
function getDetail<T extends Size>(size: T): Detail<T>;
function getDetail(size: Size = "small"): Detail<Size>{
    if(size === "small")
        return "noDetail";
    if(size === "medium")
        return "partialDetail";
    return "fullDetail";
}

Playground link

CodePudding user response:

I would add "small" as type for size and I would also add "small" as default for the generic type.

function getDetail<S extends Size = "small">(size: S | "small" = "small"):Detail<S>{
    if(size === "small")
        return "noDetail" as Detail<S>;
    if(size === "medium")
        return "partialDetail" as Detail<S>
    return "fullDetail" as Detail<S>;
}

Playground

CodePudding user response:

I would create a Enum with your different values and then create your function based on that. As you requested the return as a conditional type, i also added an Interface so the return is typesafe:

enum Size {
   "small",
   "medium",
   "large"
}
interface Detail<Size> {
    model: string
}

function getDetail(size:Size = Size.small){
    return {
        model: Size[size]
    } as Detail<typeof size>;
}

console.log(getDetail(Size.medium));
console.log(getDetail());
  • Related