繁体   English   中英

在 TypeScript 中重载联合类型

[英]Overloading union types in TypeScript

我想定义一个 function f使得 arguments 和返回类型是联合类型,但它们是绑定的,当你调用f(anInputType)时,你会得到aCorrespondingOutputType作为结果。 我尝试了以下方法,它似乎适用于 TypeScript 4.1.3。

class Input1 {
    type1Input: string = "I am type 1!";
}

class Input2 {
    type2Input: string = "I am type 2!";
}

interface Output1 {
    type1Output: string;
}

interface Output2 {
    type2Output: string;
}

export type Input = Input1 | Input2;
export type Output = Output1 | Output2;

function f(_input: Input1): Output1;
function f(_input: Input2): Output2;
function f(_input: Input): Output {
    return null as any as Output;
}

const input = new Input2();

const output = f(input);
output.type2Output // Compiles because the compiler can figure out that output is of type Output2

然而,在(我的)现实生活中,输入应该是 WebGL 对象。 同样的事情,我用Input1替换WebGLTexture和用Input2替换WebGLBuffer ,不能编译。

interface Output1 {
    type1Output: string;
}

interface Output2 {
    type2Output: string;
}

export type Output = Output1 | Output2;

function f(_input: WebGLTexture): Output1;
function f(_input: WebGLBuffer): Output2;
function f(_input: WebGLTexture | WebGLBuffer): Output {
    return null as any as Output;
}

const canvas = document.createElement("canvas")!;
const gl = canvas.getContext("webgl")!;
const input = gl.createBuffer()!;

const output = f(input);
output.type2Output // Does not compile because the compiler thinks that output is of type Output1

我错过了什么吗?

我正在阅读这些 TypeScript 问题:

以及这些其他问题:

但令我困扰的是使用现有类型(WebGL 对象)与自定义类的行为之间的差异。

实际上,我正在采用 Alex 的想法

它看起来很冗长,但有了它我可以摆脱一个单一的实现。 由于应用程序其他部分的组织方式,这对我来说有点失败。 某事,某处,会看起来很丑。 但现在这很好! 谢谢!

下面的真实代码。

export type WebGLResource =
  { texture: WebGLTexture } |
  { buffer: WebGLBuffer } |
  { program: WebGLProgram } |
  { renderbuffer: WebGLRenderbuffer } |
  { framebuffer: WebGLFramebuffer };
export type ResourceMeta = TextureMeta | BufferMeta | ProgramMeta | RenderbufferMeta | FramebufferMeta;

function getMeta(<...omitted params...> resource: { texture: WebGLTexture }, required: true): TextureMeta;
function getMeta(<...omitted params...> resource: { buffer: WebGLBuffer }, required: true): BufferMeta;
function getMeta(<...omitted params...> resource: { program: WebGLProgram }, required: true): ProgramMeta;
function getMeta(<...omitted params...> resource: { renderbuffer: WebGLRenderbuffer }, required: true): RenderbufferMeta;
function getMeta(<...omitted params...> resource: { framebuffer: WebGLFramebuffer }, required: true): FramebufferMeta;
function getMeta(<...omitted params...> resource: { texture: WebGLTexture }, required: false): TextureMeta | null;
function getMeta(<...omitted params...> resource: { texture: WebGLBuffer }, required: false): BufferMeta | null;
function getMeta(<...omitted params...> resource: { buffer: WebGLProgram }, required: false): ProgramMeta | null;
function getMeta(<...omitted params...> resource: { renderbuffer: WebGLRenderbuffer }, required: false): RenderbufferMeta | null;
function getMeta(<...omitted params...> resource: { framebuffer: WebGLFramebuffer }, required: false): FramebufferMeta | null;
function getMeta(<...omitted params...> resource: WebGLResource, required: boolean): ResourceMeta | null {
  ...
}

Typescript 是结构性的,不是标称的。 这意味着如果两种类型具有相同的形状,但名称不同,则它们被认为是相同的类型。 不幸的是, WebGLTextureWebGLBuffer似乎具有相同的形状。

如果您在 VSCode 或 typescript 操场中的任何一种类型上 cmd+click(或 ctrl+click),您可以看到它们是如何声明的。 这产生了这两个声明:

interface WebGLTexture extends WebGLObject {}
interface WebGLBuffer extends WebGLObject {}

遗憾的是,这两种类型都被简单地声明为未更改的WebGLObject Typescript 无法区分它们。 因此,当您尝试触发第二个 function 过载时,typescript 会注意到第一个过载匹配,并使用它。

很难从这个人为的代码中建议一个更好的解决方案,但您可能会在这里需要一种不同的方法。


也许您可以改为接受 object 的密钥,以明确您传入的内容? 这样不同的重载并具有不同的参数类型。

function f(_input: { texture: WebGLTexture }): Output1;
function f(_input: { buffer: WebGLBuffer }): Output2;
function f(_input: { texture?: WebGLTexture, buffer?: WebGLBuffer }): Output {
    return null as any as Output;
}

const canvas = document.createElement("canvas")!;
const gl = canvas.getContext("webgl")!;

const buffer = gl.createBuffer()!;
const texture = gl.createTexture()!;

f({ texture }).type1Output; // works
f({ buffer }).type2Output; // works

操场

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM