基于输入参数的TypeScript函数返回类型

时间:2019-01-13 02:07:08

标签: typescript

我有一些不同的接口和对象,每个接口和对象都有一个type属性。假设这些是存储在NoSQL数据库中的对象。如何基于输入参数getItem创建具有确定返回类型的泛型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);
                       ^^^^^^
}
  

类型'Circle |类型中不存在属性'radius'正方形”。

3 个答案:

答案 0 :(得分:2)

您正在寻找overload signatures

function getItems(type: "circle"): Circle[]
function getItems(type: "square"): Square[]
function getItems(type: "circle" | "square") {
    return shapes.filter(s => s.type == type);
}

在实际定义之前放置多个类型签名,使您可以列出函数签名可能属于的不同“情况”。

评论后编辑

事实证明,您想要的是可能,但是我们可能必须跳过几圈才能到达目的地。

首先,我们需要一种翻译每个名称的方法。我们希望"circle"映射到Circle"square"映射到Square,依此类推。为此,我们可以使用conditional type

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

(我使用never作为后备,希望如果您以某种方式最终以无效的类型结束,它会很快产生类型错误)

现在,我不知道像您所要求的那样对函数调用的类型进行参数化的方法,但是Typescript 确实支持通过以下方式对对象的键进行参数化: mapped typed。因此,如果您愿意使用getItems("circle")的{​​{1}}语法进行交易,我们至少可以描述该类型。

getItems["circle"]

问题是,我们现在必须构造这种类型的对象。假设您要定位ES2015(编译时为interface Keys { circle: "circle"; square: "square"; } type GetItemsType = { [K in keyof Keys]: ObjectType<K>[]; } 或更高版本),则可以使用Javascript Proxy类型。现在,不幸的是,我不知道有什么好方法可以说服Typescript我们正在做的事还可以,因此快速浏览--target es2015可以消除它的疑虑。

any

因此,您将失去对实际let getItems: GetItemsType = <any>new Proxy({}, { get: function(target, type) { return shapes.filter(s => s.type == type); } }); “功能”的类型检查,但是在呼叫站点上获得了更强大的类型检查。然后,拨打电话,

getItems

这值得吗?这取决于你。这是很多额外的语法,您的用户必须使用const circles = getItems["circle"]; for (const circle of circles) { console.log(circle.radius); } 表示法,但是它会得到您想要的结果。

答案 1 :(得分:2)

受接受的答案启发,我想出了这个。

感谢西尔维奥(Silvio)向我介绍了dataset。你觉得这怎么样?

inf

答案 2 :(得分:-2)

As you have mentioned that data is comming from No-Sql database with a type property. you can create type property as string value and change your interfaces as a class to check instanceOf in your function.

class Circle {
    type: string;
    radius: number;
}

class Square {
    type: string;
    length: number;
}

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

function getItems(type: string) {
    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) {
    if (circle instanceof Circle) {
        console.log(circle.radius);
    } else if (circle instanceof Square) {
        console.log(circle.length);
    }
}