简体   繁体   中英

how can i implement an interface with a set of values in typescript?

i'm trying to implement a interface on a class like this:

type TLanguage = "TYPE" | "SCRIPT" // Reusable values

interface AnyInterface {
    business: TLanguage
    /** MoreTypes */
}

class Anyclass implements AnyInterface{
    business = "TYPE"
}

This throws a error:

Property 'business' in type 'Anyclass' is not assignable to the same property in base type 'AnyInterface'. Type 'string' is not assignable to type 'TLanguage'.ts(2416)

Expected behavior is to whenever i set any value of business outside of the TLanguage scope, it would throw a error... It does work when i set it on a variable:

const anyConst: AnyInterface = {
    business: "TYPE" //✅
    business: "TYPEs" //❌ Type '"TYPEs"' is not assignable to type 'TLanguage'. Did you mean '"TYPE"'?ts(2820)


}

When a class implements an interface , it doesn't actually affect the type of the class. The compiler checks that the class is compatible with the interface, but it doesn't use the interface as context to give types to the class's members. This is a pain point in TypeScript, since people do expect some sort of contextual inference to happen. See microsoft/TypeScript#32082 and issues linked within for more information.

When you write class Anyclass implements AnyInterface {...} , the compiler acts very much the same as if you had just written class Anyclass {...} without implements AnyInterface . If you implement it improperly (or if the compiler infers this) you'll get an error on the Anyclass name.

Thus the problem is that class Anyclass { business = "TYPE" } causes business to be automatically widened from the literal type "TYPE" to string , since the compiler has no context to do otherwise, and implements AnyInterface doesn't change this.


And this also means that your solutions here are the same ones you'd have to use if you left out implements AnyInterface . You could explicitly annotate the type of the field :

class Anyclass implements AnyInterface {
    business: TLanguage = "TYPE"
}

or you could use a const assertion to ask the compiler not to widen the string literal (but this will infer "TYPE" and not TLanguage for the property type):

class Anyclass implements AnyInterface {
    business = "TYPE" as const
    // (property) Anyclass2.business: "TYPE"
}

or you could make it a readonly property which also hints the compiler not to widen (so it's "TYPE" ) and won't let you reassign it either:

class Anyclass implements AnyInterface {
    readonly business = "TYPE"
    // (property) Anyclass3.business: "TYPE"
}

Playground link to code

You can fix this with as const :

type TLanguage = "TYPE" | "SCRIPT" 

interface AnyInterface {
    business: TLanguage
    /** MoreTypes */
}

class Anyclass implements AnyInterface{
    business = "TYPE" as const
}

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