Typescript - type assertion does not throw an error for an invalid value

When assigning a boolean for the key, I have to use a type assertion for the variable 'day' to avoid any errors.

I don't simply do const day: Day = 2 because the value I receive is a string (dynamic value), and a type assertion is necessary.

Please review the following example and advise on how to make it throw an error:

    type Day = 0 | 1 | 2;

type TTime = {
    [K in Day]?: string
}

const day = 2 as Day;

const obj: TTime = {
    [day]: true // Expecting this to throw an error since 'boolean' is not assignable to the key
}

Here's a more detailed example of the issue I'm trying to solve. I need to extract keys from another object (default string[]) and perform type assertion.

type Day = 0 | 1 | 2;

type TTime = {
    [K in Day]?: string
}

const result: TTime = {
    2: 'I am the result'
}

const keys = Object.keys(result);

let obj: TTime;
keys.forEach((key) => {
    const k = key as unknown as Day;
    const value = result[k]; //Key is stored as string, hence needs to be type asserted
    obj = {
        [k]: true // Here, k is 2, but the value is boolean which causes error. This error can be fixed by removing the added type assertion and directly using k as 2.
    }
})

Answer №1

Encountering two limitations or issues in TypeScript is what you're currently facing... the first one isn't too severe, but it paves the way for the second major issue.


The initial problem can be found with microsoft/TypeScript#13948, where an object featuring a computed property key within a union type receives an unnecessary widening of its object type due to an index signature. Here's an example:

const v = { [Math.random() < 0.5 ? "a" : "b"]: 123 };
// const v: { [x: string]: number;}

The variable v will be either {a: 123} or {b: 123} at runtime. Ideally, TypeScript should infer the type of v as something like {a: number} | {b: number}, but instead, it infers { [x: string]: number }, losing track of the specific key names that v holds.

In your situation, you have

const obj = { [day]: true };
// const obj: { [x: number]: boolean; }

where obj has been expanded from something like

{0: boolean} | {1: boolean} | {2: boolean}
all the way to {[x: number]: boolean}, disregarding the reference to Day.

This issue isn't a disaster because the actual type is at least somewhat compatible with the desired type, although it's not the most optimal solution.


The second bug identified is microsoft/TypeScript#27144, which allows an object type with an index signature to be assigned to a type containing all optional properties, even if the values are incompatible. This scenario proves to be quite problematic:

const v: { [k: string]: number; } = {a: 1};
const x: { a?: string } = v; // no error!

The type of v enforces that all properties under the index signature contain a numeric value. On the other hand, the type of x specifies a single optional property holding a string value if present. These types shouldn't align, yet they do, allowing an erroneous assignment from v to x without any warning from the compiler.

In your case, consider this:

const p: TTime = o; // no error

Here, the TTime type includes all optional properties and is deemed compatible with the index-signature type of o, despite the mismatch between boolean and string.

These two issues intertwine to create the problem you are encountering. The best course of action would likely involve waiting for microsoft/TypeScript#27144 to be remedied. Visiting the issue and showing support might help, but there's no guarantee on when or if it will be addressed.

Until then, workarounds are necessary.

An approach to address microsoft/TypeScript#13948 could involve creating a helper function to provide more precise type assertions rather than settling for the generic index signature type. An example is shown below:

const kv = <K extends PropertyKey, V>(
  k: K, v: V
) => ({ [k]: v }) as { [P in K]: { [Q in P]: V } }[K];

The kv function generates an object with a key-value pair based on the inputs provided:

const y = kv("a", 123);
// const y: { a: number; }

If applied to a union type for the key, we get an output reflecting the potential variations:

const w = kv(Math.random() < 0.5 ? "a" : "b", 123);
// const w: {a: number} | {b: number}

This strategy can be used to redefine obj:

const obj = kv(day, true);
// const obj: { 0: boolean; } | { 1: boolean; } | { 2: boolean; }

By removing the index signature from obj, we avoid running into issues akin to microsoft/TypeScript#27144:

const obj: TTime = kv(day, true); // error,
// boolean is not assignable to string

The compiler recognizes the incompatibility between

{0: boolean} | {1: boolean} | {2: boolean}
and TTime since boolean doesn't align with string, as intended.

Access the Playground link for code samples here

Answer №2

By following this approach, the error message will be displayed;


type Month = 0 | 1 | 2;

type MTime = {
    [M in Month]?: string
}

const month: Month = 2;

const object: MTime = {
    [month]: true // We anticipate an error being thrown since 'boolean' cannot be assigned to a key
}

Answer №3

The problem with the code lies in the type assertion made on day. It is unnecessary because primitives are immutable in JavaScript and TypeScript. By using the const keyword to declare a variable, like in this case where const day = 2;, TypeScript automatically infers the literal type of the number as 2.

This behavior can be observed in tools like TS Playground

type Day = 0 | 1 | 2;

type TTime = { [K in Day]?: string };

// This is the same type as above, utilizing some type utility methods:
// https://www.typescriptlang.org/docs/handbook/utility-types.html
// type TTime = Partial<Record<Day, string>>;

// Avoid using a type assertion here. (https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions)
// If you prefer an annotation, you could write it this way:
// const day: Day = 2;
const day = 2;

const obj: TTime = {
  [day]: true, /*
  ~~~~~
  The computed property's value is of type 'boolean', which cannot be assigned to type 'string'.(2418) */
};

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

Neglectful TypeScript null checks overlooking array.length verification

When TypeScript is compiled with strict null checks, the code snippet below does not pass type checking even though it appears to be correct: const arr: number[] = [1, 2, 3] const f = (n: number) => { } while (arr.length) { f(arr.pop()) } The comp ...

Deploying CSS/JS files in Magento 2 is a crucial

Hello, I recently set up magento2 with the sample data included. After attempting to deploy static css/JS using the command php bin/magento setup:static-content:deploy, I didn't receive any errors but the issue persists. Additionally, I am unable to l ...

Encountering a 403 error when attempting to upload files to Google Cloud Storage (GCS) using Signed URLs

The main aim is to create a signed URL in the api/fileupload.js file for uploading the file to GCS. Then, retrieve the signed URL from the Nextjs server through the nextjs API at localhost://3000/api/fileupload. Finally, use the generated signed URL to upl ...

Encountering a node module issue when implementing graphql in a TypeScript project

I encountered issues when attempting to utilize the @types/graphql package alongside TypeScript Node Starter node_modules//subscription/subscribe.d.ts(17,4): error TS2314: Generic type AsyncIterator<T, E>' requires 2 type argument(s). node_modu ...

What is the mechanism for invoking functions defined with the arrow syntax in Angular?

Referencing this code snippet from the tutorial at https://angular.io/tutorial/toh-pt4, specifically within the hero.component.ts file: getHeroes(): void { this.heroService.getHeroes() .subscribe(heroes => this.heroes = heroes); } After analyz ...

Webpack, TypeScript, and modules are set to "esnext," resulting in a change to undefined

In my setup, I am using webpack with typescript (via ts-loader). To enable code splitting in webpack, it is necessary to adjust the module setting to esnext in the tsconfig file: // tsconfig.json { "compilerOptions": { "module": ...

"Step-by-step guide on deactivating SmartyStreets/LiveAddress with an onclick function

I've recently taken over a SquirrelCart shopping cart application that utilizes SmartyStreets/LiveAddress code, and I'm struggling to figure out how to disable the verification process when copying billing address fields to shipping address field ...

Why is it that when I store a DOM element reference in a JavaScript Array, I cannot reuse that stored reference to add an event listener

I have a little confusion and I hope someone can help me out. I am facing an issue with dynamically created buttons, where each button has a unique id. To keep track of these buttons in a well-organized manner, I store their references using a simple two-d ...

Error in Layout of Navigation Panel and Tabbed Pages

Working on a school project, I encountered a challenge. I found two useful solutions from the W3 website - a sticky navigation bar and the ability to include tabs on a single page for a cleaner presentation of information. However, when trying to implement ...

Show the current time using Moment.js

I am currently working on developing a clock component that displays the current time in real-time. Issue: The initial time is correctly displayed when the page loads (HH:mm A), but the clock does not update dynamically. clock.component.ts : import { ...

Update header component dynamically upon successful login with Angular 11

I am currently using Angular 11 and facing an issue with displaying the username in the header component. The header loads before the login component, which results in the user data being stored in local storage only after the login component is loaded. As ...

Exploring the inner components of an entity without the need for external tools

I am currently enhancing TypeScript usage in a project by implementing generics. The challenge I am facing involves dealing with a complex object retrieved from the backend, which consists of a class with numerous attributes, most of which are classes them ...

Invoke JavaScript when the close button 'X' on the JQuery popup is clicked

I am implementing a Jquery pop up in my code: <script type="text/javascript"> function showAccessDialog() { var modal_dialog = $("#modal_dialog"); modal_dialog.dialog ( { title: "Access Lev ...

The correct way to update component state when handling an onChange event in React using Typescript

How can I update the state for 'selectedValues' in a React component called CheckboxWindow when the onChange() function is triggered by clicking on a checkbox? export const CheckboxWindow: React.FC<Props> = props => { const [selected ...

Securing a single component within a named view router in Vue.js

Within my routes configuration, I have a named view route set up: let routes = [ { name: "home", path: '/', components: { default: Home, project: ProjectIndex } } ] The goal is to ...

Creating Typescript packages that allow users to import the dist folder by using the package name

I am currently working on a TypeScript package that includes declarations to be imported and utilized by users. However, I have encountered an issue where upon publishing the package, it cannot be imported using the standard @scope/package-name format. I ...

Direct your attention to the final item in a visible array within a ReactJS component

Currently, I'm in the process of developing a chat application using reactjs and am faced with the challenge of adjusting focus to the latest message whenever a new one is added to the array. The structure of my react chat window is as follows: < ...

Leveraging the power of jQuery/javascript in conjunction with Google Forms

Currently, I am attempting to utilize jQuery and JavaScript with an iframe that contains a Google form. The code snippet is displayed below: <body> <iframe id="myFormFrame" src="https://docs.google.com/forms/d/smfjkafj809890dfafhfdfd/viewform?emb ...

What is the best way to dynamically change className in React.js?

I am working on a Collection component where I need to change the classNames of both the Collection component and its first sibling component when a div is clicked. Even though I tried using UseState, I could only manage to change the className of the cur ...

Internet Explorer freezing when running selenium executeScript

Hey everyone, I've spent the past couple of days scouring the internet trying to find a solution to my modal dialog problem. There's a wealth of helpful information out there and everything works perfectly fine except for Internet Explorer. Speci ...