HiveBrain v1.2.0
Get Started
← Back to all entries
patterntypescriptCritical

TypeScript function return type based on input parameter

Submitted by: @import:stackoverflow-api··
0
Viewed 0 times
typescriptinputreturnfunctionparametertypebased

Problem

I have a few different interfaces and objects that each have a type property. Let's say these are objects stored in a NoSQL db. How can I create a generic getItem function with a deterministic return type based on its input parameter type?

interface Circle {
    type: "circle";
    radius: number;
}

interface Square {
    type: "square";
    length: number;
}

const shapes: (Circle | Square)[] = [
    { type: "circle", radius: 1 },
    { type: "circle", radius: 2 },
    { type: "square", length: 10 }];

function getItems(type: "circle" | "square") {
    return shapes.filter(s => s.type == type);
    // Think of this as items coming from a database
    // I'd like the return type of this function to be
    // deterministic based on the `type` value provided as a parameter. 
}

const circles = getItems("circle");
for (const circle of circles) {
    console.log(circle.radius);
                       ^^^^^^
}



Property 'radius' does not exist on type 'Circle | Square'.

Solution

Conditional Types to the rescue:

interface Circle {
    type: "circle";
    radius: number;
}

interface Square {
    type: "square";
    length: number;
}

type TypeName = "circle" | "square"; 

type ObjectType = 
    T extends "circle" ? Circle :
    T extends "square" ? Square :
    never;

const shapes: (Circle | Square)[] = [
    { type: "circle", radius: 1 },
    { type: "circle", radius: 2 },
    { type: "square", length: 10 }];

function getItems(type: T) : ObjectType[]  {
    return shapes.filter(s => s.type == type) as ObjectType[];
}

const circles = getItems("circle");
for (const circle of circles) {
    console.log(circle.radius);
}


Thanks Silvio for pointing me in the right direction.

Code Snippets

interface Circle {
    type: "circle";
    radius: number;
}

interface Square {
    type: "square";
    length: number;
}

type TypeName = "circle" | "square"; 

type ObjectType<T> = 
    T extends "circle" ? Circle :
    T extends "square" ? Square :
    never;

const shapes: (Circle | Square)[] = [
    { type: "circle", radius: 1 },
    { type: "circle", radius: 2 },
    { type: "square", length: 10 }];

function getItems<T extends TypeName>(type: T) : ObjectType<T>[]  {
    return shapes.filter(s => s.type == type) as ObjectType<T>[];
}

const circles = getItems("circle");
for (const circle of circles) {
    console.log(circle.radius);
}

Context

Stack Overflow Q#54165536, score: 264

Revisions (0)

No revisions yet.