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,
- I don't want to force people to pass a parameter.
- 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";
}
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>;
}
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());