Home > OS >  Typescript Overriding Type of String Literals
Typescript Overriding Type of String Literals

Time:02-01

I currently have the following setup:


enum EnumA {
    A,
    AYE
}

enum EnumB {
    B,
    BEE
}

type A = keyof typeof EnumA
type B = keyof typeof EnumB

type AB = A | B

interface IBase {
    foo: (...ok: AB[]) => IBase
}

abstract class Base implements IBase {
    public foo(...ok: AB[]): Base {
        return this
    }
}

class BaseA extends Base implements IBase {
    public override foo(...ok: A[]): Base {
        return super.foo(...ok)
    }
}

class BaseB extends Base implements IBase {
    public override foo(...ok: B[]): Base{
        return super.foo(...ok)
    }
}

const myA: BaseA = new BaseA()

I expect that overriding the type of "ok" in the subclasses of Base and implementation of IBase should work, however typescript complains:

Property 'foo' in type 'BaseA' is not assignable to the same property in base type 'IBase'. Type '(...ok: ("A" | "AYE")[]) => Base' is not assignable to type '(...ok: AB[]) => IBase'. Types of parameters 'ok' and 'ok' are incompatible. Type 'AB' is not assignable to type '"A" | "AYE"'. Type '"B"' is not assignable to type '"A" | "AYE"'.ts(2416)

Any ideas what is going wrong here? I'd like to call myA.foo() with type declarations that represent the type A, being a subset of type AB.

Any help would be greatly appreciated.

CodePudding user response:

You are declaring an interface that expects A|B as an argument to a method. Any class that implements that interface must respect the contract. This means that passing either A or B must be acceptable. When you are narrowing the type you are actually violating the contract for either A or B and this is not ok. What you probably want are generics.

enum EnumA {
    A,
    AYE
}

enum EnumB {
    B,
    BEE
}

type A = keyof typeof EnumA
type B = keyof typeof EnumB

type AB = A | B

interface IBase <T> {
    foo: (ok: T) => IBase<T>
}

abstract class Base <T> implements IBase<T> {
    public foo(...ok: T[]): Base<T> {
        return this
    }
}

class BaseA extends Base<A> implements IBase<A> {
    public override foo(...ok: A[]): Base<A> {
        return super.foo(...ok)
    }
}

class BaseB extends Base<B> implements IBase<B> {
    public override foo(...ok: B[]): Base<B>{
        return super.foo(...ok)
    }
}

const myA: BaseA = new BaseA()

Here's a playground link

  • Related