Is Typescript's Union Type Flawed? (Issues Arise When Combining Object Types and Object Literals)

https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#unions-with-common-fields

keyof (A|B) = (keyof A) & (keyof B)

Showing an example:

interface Bird {
  fly(): void;
  layEggs(): void;
}
   
interface Fish {
  swim(): void;
  layEggs(): void;
}
  
declare function getSmallPet(): Fish | Bird;
  
let pet = getSmallPet();
pet.layEggs();

1 Example -GOOD

2 Example -GOOD

interface Birds {
    flys: boolean;
    layEggs: string;
}

interface Fish {
    swim: number;
    layEggs: string;
}

declare let k: Fish & Birds;

k = { flys:true, layEggs:"", swim:3}

3 Example -GOOD

4 Example -BROKEN

Everything is perfect until the 4th Example... It's confusing. Shouldn't it be k = { layEggs: ""} ????

PROOF:

interface Bird {
    fly: number;
    layEggs: string;
}

type g1 = keyof Bird

interface Fish {
    swim: boolean;
    layEggs: string;
}

type g2 = keyof Fish


type PetALL= keyof( Bird & Fish )
// let all1: EmployeeAll = "fly" 
// let all2: EmployeeAll = "email" 
// let all3: EmployeeAll = "layEggs" 

type Pet = keyof( Bird | Fish )
// let only: Employee = "layEggs"

EDITOR USED https://www.typescriptlang.org/play (default settings)

CLARIFICATION OF THE QUESTION

The question is why is the type allowed in the following case:


interface Birds {
    flys: boolean,
    layEggs: string
}

interface Fish {
    swim: number,
    layEggs: string,
}

declare let k: Fish | Birds;

k = {}

Shouldn't it just be k = { layEggs: ""} ??

As seen in 4 Example -BROKEN

AND WHY DOES THIS SYNTAX WORK

5 Example -FIX

5 Example -FIX

Answer №1

When utilizing a union in TypeScript, the rule regarding object literals seems to break down. Switching to the Object type causes TypeScript to raise expected complaints.

It's worth noting that TypeScript has its quirks and unexpected behaviors, but overall, it remains one of the best options available.

1 WORKING WITH TYPES


type Birds = {
    flys: boolean;
    layEggs: string;
}

type Fish = {
    swim: number;
    layEggs: string;
}

type UnionType = Fish | Birds

//Using Object Literal with Object Type is acceptable 
let k1: Birds= {flys:true,layEggs:"yes"}

//Using Object Literal with Object Type is acceptable 
let k2: Fish= {swim:100,layEggs:"of course"}

//DO NOT use Object Literal with Union Type as it breaks
let k3: UnionType={flys:true, layEggs:"yeah",swim:22}

//Managing union types with Function Overloading:
declare let bla: UnionType;
bla.layEggs = "yeah"

2 USING INTERFACES


interface Birds {
    flys: boolean;
    layEggs: string;
}

interface Fish {
    swim: number;
    layEggs: string;
}

type UnionType = Fish | Birds

//Using Object Literal with Object Type is acceptable 
let k1: Birds= {flys:true,layEggs:"yes"}

//Using Object Literal with Object Type is acceptable 
let k2: Fish= {swim:100,layEggs:"of course"}

//DO NOT use Object Literal with Union Type as it breaks
let k3: UnionType={flys:true, layEggs:"yeah",swim:22}

//Handling union types with function overloading:
declare let bla: UnionType;
bla.layEggs = "yeah"

Posing a question proved challenging, special thanks to @vlaz, @yury-tarabanko, @jcalz, @dane-brouwer

  1. typescript type is lost on an object literal assign using a union type
  2. TypeScript: Discriminated Unions with optional values
  3. There are numerous other valuable resources online...

This behavior is specific to TypeScript for now. I will refrain from using Object Literals and opt for the Function Overloading approach instead.

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

When attempting to loop through an Observable that is of type array using *ngFor, the error message "Cannot read property 'subscribe' of undefined

I can't figure out why this isn't working, even though it seems straightforward. I'm using a service that gives me an observable. There are a few rxjs streams connected to it in the following way: search(searchTerm: string { this.search ...

Tips for converting "module.exports" from JavaScript to TypeScript?

How can I transform the JavaScript concept of module.exports into TypeScript? The original JavaScript code with module.exports is shown below: module.exports = { discord: { clientID: "", clientSecret: "", cl ...

Looking to generate a temporary query in VSTS? The createquery() function is perfect for this task, but be cautious as using getquery()

let searchQuery = "Retrieve [System.Id], [System.Title], [System.State] For WorkItems Where [System.WorkItemType] = 'Bug' sort by [Microsoft.VSTS.Common.Priority] in ascending order, then by [System.CreatedDate] ...

Retrieving and showcasing the JSON data from the backend as a dropdown menu within an Ionic application

I am currently working on a project with Ionic framework and I have a requirement to fetch JSON data from the backend using an API call and display this content as dropdown options in the front end. Below, you can see the API call code along with the JSON ...

Issue with nullable return types in Typescript not being implemented correctly

Upon reviewing this snippet: export interface ICollectionService { get(id: string): Promise<Collection | null>; } const collection = await collectionService.get(collectionAddress); I noticed that the collection variable in my IDE is now displayin ...

What steps can be taken to expand the axis space in recharts to accommodate an additional label?

I'm struggling to display an additional label below my X-Axis label as it keeps getting clipped off. https://i.sstatic.net/ftI7w.png Below is the code snippet for my XAxis: <XAxis type="number" tick={<CustomizedNumberTick lang={props ...

Simplify if statements by eliminating repetition

I have been tasked with refactoring the code below and I have already done so (check the image for reference). However, my supervisor is still not satisfied with the changes, LOL. const { appTargetId, appUserTargetId, appUserId } = buildIndexKeys(input); ...

What causes the interface property to be undefined when attempting to assign a value?

Take a look at the structure of my basketball Player class and the ITeam & IStats interfaces: export class Player { id: string; firstName: string; lastName: string; position: string; team: ITeam; stat: IStats; get fullName(): string { ...

Exploring Angular 2 Tabs: Navigating Through Child Components

Recently, I've been experimenting with trying to access the HTML elements within tabs components using an example from the Angular 2 docs. You can view the example here. Here is a snippet of my implementation: import {Component, ElementRef, Inj ...

Obtaining the date without the time in TypeScript and MongoDB

I'm working with TypeScript and have the following code snippet: const EmployeeDetailsSchema: mongoose.Schema = new mongoose.Schema({ employeeId: { type: String }, advance: { lastAdvanceClosedOn: { type: String }, pending: { type: String ...

Enhancing TypeScript with Generic Proxyify Functionality

I'm attempting to enclose a basic interface provided through a type generic in order to alter the return value of each function within the interface. For instance: interface IBaseInterface { test(a?: boolean, b?: number): Promise<boolean>; ...

Looking to identify the type of a adorned class in Typescript?

Consider the following scenario: return function IsDefined(object: any, propertyName: string) { .... ] We then go ahead and decorate a property like this: class Test { @IsDefined() p1: String = ""; } Now, when we execute a test inside the ...

Changing the value of a property in an object based on the object's attribute in JavaScript

I have a JSON data set like this: inputData = [ { id : 179849, name : alex , lastname: sanchez}, { id : 788539, name : Paul, lastname: bearer}, { id : 282169, name : Jean, lastname: nobel}, ... { id : 632785, name : Maria, lastname: parak} ] I am looking ...

Troubleshooting the inclusion of nodemon in package.json

I am interested in implementing nodemon to automatically recompile the project when there are changes made to the code during development. package.json { "name": "insurance-web-site", "version": "0.1.0", " ...

Error TS2694 is being caused by Electron Typescript Material-UI withStyles because the namespace "".../node_modules/csstype/index"" does not have an exported member called 'FontFace'

While I am experienced with Material-UI, I am relatively new to Electron and using React, TypeScript, and Material-UI together. Recently, I encountered an error while attempting to create an electron boilerplate code for future project initialization. Init ...

How to Utilize Knockout's BindingHandler to Integrate JQuery.Datatables Select Feature?

I've developed a custom KO bindingHandler (view it here) to assist in updating the DataTable. The documentation for JQuery.DataTable.Select regarding how to access data requires a handle. You can see the details here. var table = $('#myTable&a ...

Locate the initial occurrence of a duplicated element within an array

I need to check for duplicate values in an array element. Within my array, I have multiple objects like {S:1,R:2,V:3}. My goal is to display an alert message if there are duplicate values for the "S" element in that array. My Approach: var arr=[{S:1,R:2 ...

Utilizing React Typescript to Efficiently Manage Multiple Checkboxes within a List

I'm working on a scenario where I have to manage multiple checkboxes in a list Only one checkbox can be selected at a time For example, if I toggle on Checkbox 1 and then click on Checkbox 2 - I want to automatically toggle off Checkbox 1 as I toggl ...

Determining the data type of a class member by analyzing a different member

Currently, I have a TypeScript class called Foo that consists of fieldOne: number | string and fieldTwo. Specifically, when fieldOne is a number, then fieldTwo will be of type Bar[]; whereas if fieldOne is a string, then fieldTwo becomes Baz[]. Both fields ...

How to troubleshoot the issue of "Error: (SystemJS) module is not defined" in Angular 2?

I am a beginner in the world of Angular2. It is known that in Angular2, there is a way to reference a file using a relative path by defining moduleId : module.id in the component meta data. However, I have tried doing it this way and keep encountering the ...