Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Overriding methods on built-ins based on type

Let’s say I have a type to denote a language code, for example:

type LangCodeUpper = 'EN' | 'DE' | 'ES';
type LangCodeLower = Lowercase<LangCodeUpper>;

The problem I’m trying to solve is the following:

const lang: LangCodeUpper = 'EN';
const lowerLang = lang.toLowerCase(); //this is `string`, while I want `LangCodeLower`

So far, I’ve got:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

type LangCodeUpperDecl = 'EN' | 'DE' | 'ES';
type LangCodeLowerDecl = Lowercase<LangCodeUpper>;

type LanguageCaseImpl<T> = Omit<T, 'toLowerCase' | 'toUpperCase'> & {
    toUpperCase?: () => LangCodeUpperDecl;
    toLowerCase?: () => LangCodeLowerDecl;
};

type LangCodeUpper = LanguageCaseImpl<LangCodeUpperDecl>;
type LangCodeLower = LanguageCaseImpl<LangCodeLowerDecl>;

Which kind of works, since this successfully assigns the correct type when calling one of the two methods. But it also introduces new problems, namely:

function a(lang: LangCodeUpper) {
    return this.title[lang]; //TS2538: Type 'LangCodeUpper' cannot be used as an index type.
}

The obvious solution is to introduce some kind of wrapper object, that would hold the value and provide methods like toCodeUpper/toCodeLower. Yet I would much rather stick with a basic type – is there a better way to override the methods for these types?

>Solution :

As you have noticed String#toLowerCase() and String#toLowerCase() both return arbitrary strings and don’t use the built in utility types Lowercase and Uppercase. There is an open GitHub issue (microsoft/TypeScript#44268) which addresses this behavior but I don’t think we can expect any news on it very soon.

As of now, you can extend the global String interface with a custom overload.

interface String {
    toLowerCase<T extends string>(this: T): Lowercase<T>;
}

type LangCodeUpper = 'EN' | 'DE' | 'ES';
type LangCodeLower = Lowercase<LangCodeUpper>;

const lang: LangCodeUpper = 'EN';
const lowerLang = lang.toLowerCase();
//    ^? const lowerLang: "en"

TypeScript Playground

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading