简体   繁体   中英

Why does my interface fail to extend a union type in typescript

I have a type which is the union of two interfaces:

interface Foo {
  a: string
}
interface Bar {
  b: string
}
type Baz = Foo | Bar;

I want to extend it, but it gives me an error:

interface Qux extends Baz { // error!
  // ---------------> ~~~
  // An interface can only extend an object type or 
  // intersection of object types with statically known members.
  c: string;
}

interface Qux extends Baz { // error!
  c: string;
}

Why do I get that error and what can I do about it?


Original version of question with more context:

I wrote a type file to augment the Palette type defintion file in an npm library. (Material UI to be specific). If I try to have ODPaletteColorOptions extend PaletteColorOptions as shown in my code below, this does not successfully extend the PaletteColorOptions interface. Here is the outcome: apparently, the ODPaletteColorOptions interface only has the additional keys: 在此处输入图像描述

createPalette.d.ts
------------------
import { Palette as MuiPalette, PaletteColorOptions } from "@mui/material/styles/createPalette";

declare module "@mui/material/styles/createPalette" {

    export interface ODPaletteColorOptions extends PaletteColorOptions {
        1000: string,
        white: string,
        black: string,
        0: string,
        Search: string
    }

    export interface Palette extends MuiPalette{
        neutrals: ODPaletteColorOptions,
    };
}

Here is the definition of PaletteColorOptions from the existing MUI type definition file.

export type PaletteColorOptions = SimplePaletteColorOptions | ColorPartial;

where

export interface SimplePaletteColorOptions {
  light?: string;
  main: string;
  dark?: string;
  contrastText?: string;
}
export type ColorPartial = Partial<Color>;
export interface Color {
  50: string;
  100: string;
  200: string;
  300: string;
  400: string;
  500: string;
  600: string;
  700: string;
  800: string;
  900: string;
  A100: string;
  A200: string;
  A400: string;
  A700: string;
}

However, if instead of extending PaletteColorOptions, I explicitly extend SimplePaletteColorOptions and ColorPartial, then the extension works.

import { Palette as MuiPalette, SimplePaletteColorOptions, ColorPartial } from "@mui/material/styles/createPalette";

declare module "@mui/material/styles/createPalette" {

    export interface ODPaletteColorOptions extends SimplePaletteColorOptions, ColorPartial {
        1000: string,
        white: string,
        black: string,
        0: string,
        Search: string
    }

    export interface Palette extends MuiPalette{
        neutrals: ODPaletteColorOptions,        
    };
}

Why does the first approach fail?

In order for the compiler to process interface types (as well as instances of class types which can be seen as having an interface of the same name as the class), they need to have a single set of statically known keys . Union types have multiple sets of keys, so you can't extend them into interfaces. See microsoft/TypeScript#13604 for a description of what types of things can be extended from (eg, object types and intersections of such types) as well as some things that cannot be (eg, generic type parameters).

That's why you would be allowed to extend Foo or Bar separately, but not the union Foo | Bar Foo | Bar .


Luckily, you can get much the same effect with intersection types , as described in the TS Handbook documentation for differences between type aliases and interfaces :

type Qux = Baz & { c: string }; // okay

Now a Qux acts like a Baz with a string -valued c property, which is presumably what you wanted to see:

function processQux(q: Qux) {
  console.log(("a" in q ? q.a : q.b).toUpperCase() + " " + q.c);
}

const q: Qux = { a: "hey", c: "you" } // okay
processQux(q) // HEY you
processQux({ b: "it's", c: "me" }) // IT'S me
processQux({ c: "oops" }) // error! 
// Argument of type '{ c: string; }' is not assignable to parameter of type 'Qux'.
  

Playground link to code

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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