Derive a subset Union from a Union in Typescript

Here is a scenario with a Union type I'm working with;

type MyUnionType = 'foo' | 'bar' | 'baz'

What I need to do is create a new Union called MySubUnion, which will be a subset of the original;

type MySubUnion = 'foo' | 'bar'

However, I want MySubUnion to only include values that are present in MyUnionType

type MySubUnion = 'foo' | 'bas' // => Error Type String 'b...

Answer №1

When you restrict a union to only include certain elements, it is known as subtyping. In the world of TypeScript, declaring A extends B signifies that type A is a subtype of type B. This concept might be confusing at first glance since removing elements from a union actually makes the type more specific, hence making it a subtype. The use of the word "extends" in this context may seem counterintuitive, but that's just how TypeScript works.

Unfortunately, when it comes to narrowing down type aliases using extends, the functionality is not supported the same way it is for interfaces. Attempting to do so with the syntax below will result in an error:

// Incorrect TypeScript syntax, avoid using:
type MySubUnion extends MyUnionType = 'foo' | 'bar'; // should work
type MySubUnion extends MyUnionType = 'foo' | 'bas'; // should fail

As a workaround, you can create a custom type function called Extends<T, U>, which will resolve to U only if U can be extended by T:

type Extends<T, U extends T> = U;

With this new function in place, you can modify the invalid code into proper TypeScript syntax like so:

type MySubUnion = Extends<MyUnionType, 'foo' | 'bar'>; // Compiles without errors

And similarly:

type MySubUnion = Extends<MyUnionType, 'foo' | 'bas'>; // Error:
// Type '"bas"' is not assignable to type 'MyUnionType'.

Hoping this explanation helps clarify things for you! Best of luck!

Answer №2

To implement this, you can utilize the utility type called Exclude<Type, Union>.

type MyUnionType = 'apple' | 'orange' | 'banana'

type SubType = Exclude<MyUnionType, 'banana'>

// SubType will be 'apple' | 'orange'

Answer №3

One possible alternative is to reverse the order of declaration, although it may seem unusual given the specific names used here.

type OtherUnion = 'foo' | 'bar';
type AnotherType = OtherUnion | 'baz';

This approach leans more towards combining union types.

Answer №4

Referencing the content of this discussion:

Is it feasible to expand types in Typescript?

You have the option to simply declare it as follows:

type ExtendedSubType = OriginalType & ('foo' | 'bar');

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Accessing properties using bracket notation in Typescript is a powerful feature that

I wish to retrieve a typed object using bracket notation like this: interface IFoo { bar: string[]; } var obj: IFoo = { bar: ["a", "b"] } var name = "bar"; obj[name]. // type information lost after dot As per the specification 4.10, it seems to be t ...

ESLint does not recognize the types from `@vuelidate/core` when importing them

When working with TypeScript, the following import statement is valid: import { Validation, ValidatorFn } from '@vuelidate/core' However, this code triggers an error in ESLint: The message "ValidatorFn not found in '@vuelidate/core' ...

The TypeScript optional callback parameter is not compatible with the anonymous function being passed to it

Encountering an issue with TS callbacks and function signatures. Here is my scenario: ... //inside a class //function should accept a callback function as parameter refreshConnection(callback?: Function) { //do something //then ca ...

How do I retype an interface from a dependency?

It's difficult to put into words, so I have created a repository for reference: https://github.com/galenyuan/how-to-retyping My goal is to achieve the following: import Vue from 'vue' declare module 'vuex/types/vue' { interfac ...

Ways to manage drag and drop functionality within Cypress when traditional Cypress techniques are not effective

I need help with the drag and drop function in Cypress. I have tried three different methods but none of them seem to work. I have included my code below, which is not functioning as expected. Does anyone have any suggestions on what might work better in t ...

Enhancing Hapi.js server functions with TypeScript: A guide

One way to enhance the functionality of the hapi module by adding type checking for server methods is shown in the following code snippet: import { Server } from 'hapi'; declare module 'hapi' { export interface Server { m ...

How can Angular developers properly implement token refreshing in their applications?

Recently, I've been struggling with implementing a logic in my code. I have a specific requirement: Whenever there is a signed request (signed - means it has a JWT token for authenticated users) made to the API backend, the API backend may respond w ...

Modifying a property in a nested layout through a page in Next.js 13

Currently, I am facing a challenge in updating the page title within a nested layout structure. app/docs/layout.tsx: export const DocsLayout = ({children}:{children: React.ReactNode}) => { return ( <> <header> ...

``Changing the value of a class variable in Angular 2 does not result in the

I am facing an issue with a component that contains a variable called myName export class ConversationComponent implements OnInit { private myName: string; showNames(name) { this.myName=name; } } The value is assigned using the showNames() m ...

Utilize TypeScript to access scope within a directive

Is there a way to access the controller's scope properties using my custom TypeScript directive? For example, in this snippet below, I am trying to retrieve and log scope.message: /// <reference path="typings/angularjs/angular.d.ts" ...

Error: Cannot access Angular 5 Title service at this time

When attempting to change the page title using BrowserModule, I encountered an issue. I added the BrowserModule and Title in the application module as shown here: https://angular.io/guide/set-document-title However, in a child module where I tried to add ...

What is the best way to output data to the console from an observable subscription?

I was working with a simple function that is part of a service and returns an observable containing an object: private someData; getDataStream(): Observable<any> { return Observable.of(this.someData); } I decided to subscribe to this funct ...

Tips on integrating TypeScript into JavaScript

Currently, I am working with a node.js server.js file. var http = require('http'); var port = process.env.port || 1337; http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res ...

Custom "set attribute" feature in TypeScript

One issue I faced was resolved by creating the function shown below : function setProperty<T extends Record<string, string>>(obj: T, key: keyof T) { obj[key] = "hello"; } However, when I tried to compile the code, I encountered an ...

NodeJS on Cloudlinux requires that the node modules for applications be stored in a distinct folder (virtual environment) designated by a symbolic link known as "node_modules"

I recently encountered an issue while trying to deploy my Nodejs/TypeScript web application on my cpanel shared hosting. The error I received stated: * Cloudlinux NodeJS Selector requires the node modules for the application to be stored in a separate f ...

Sorting TypeScript types by required properties first

Can anyone recommend a plugin or ESLint rule that can automatically sort types by assuming required fields come first, followed by optional fields? Here's an example before: type TExampleSorting = { title?: string; value?: number; text: string; ...

How can I implement a user notification service using rxjs within Angular?

As a newcomer to reactive programming, I am exploring ways to create an Angular service that can present notifications to the user. Check out what I have accomplished so far: https://stackblitz.com/edit/angular-rxjs-notifications?file=app%2Fapp.component. ...

Angular2: Exploring the Differences Between Observable.fromEvent and Button Click

As I work with my code, I have noticed that I am using both <button (click)="callsomefucntion" /> and Observable.fromEvent<MouseEvent>(button.nativeElement.'click') interchangeably. I am curious to understand the distinction between ...

The mystery of the undefined return value in my Ionic v4 get function

I attempted to retrieve my location by saving the latitude and longitude, but my declared variable isn't returning anything. Take a look at my code snippet: public device_location: any = {}; constructor(private geolocation: Geolocation) { this.s ...

Customizing Angular Forms: Set formcontrol value to a different value when selecting from autocomplete suggestions

How can I mask input for formControl name in HTML? When the autocomplete feature is displayed, only the airport's name is visible. After users select an airport, I want to show the airport's name in the input value but set the entire airport obje ...