TypeScript's type casting will fail if one mandatory interface property is missing while an additional property is present

While running tsc locally on an example file named example.ts, I encountered some unexpected behavior. In particular, when I created the object onePropMissing and omitted the property c which is not optional according to the interface definition, I did not receive any errors.

Subsequently, I added another example called oneExtraProp where an additional property was included, anticipating a failure due to the extra property.

To my surprise, TSC flagged an error for extraAndOneMissing even though I expected it to pass based on the previous examples.

interface InterfaceEverythingRequired {
  a: string;
  b: string;
  c: number;
}
// This should be fine, and indeed it is
const allPropsPresent = { a: 'a', b: 'b', c: 1 } as InterfaceEverythingRequired;
// Expected warning for missing prop 'c', but no error occurs
const onePropMissing = { a: 'a', b: 'b' } as InterfaceEverythingRequired;
// Expected warning for extra prop, but TSC does not flag it
const oneExtraProp = { a: 'a', b: 'b', c: 3, extraProp: 'no-issues' } as InterfaceEverythingRequired;
// Despite no warnings in the previous two cases, TSC raises an error here
const extraAndOneMissing = { a: 'a', b: 'b', extraProp: 'what?' } as InterfaceEverythingRequired;

I am puzzled by this behavior. Why are errors being inconsistently detected?

The specific error message received reads:

Type '{ a: string; b: string; extraProp: string; }' cannot be 
converted to type 'InterfaceEverythingRequired'.
  Property 'c' is missing in type '{ a: string; b: string; 
extraProp: string; }'.

Answer №1

Avoid using the as keyword to assign types to your variables. It's better to directly specify the types instead.

const onePropMissing: InterfaceEverythingRequired = { a: 'a', b: 'b' }; // This will result in an error

In TypeScript, type casts work as type assertions and can lead to unexpected results due to its structural type system, especially for those transitioning from a nominally typed language.

You can find solutions to common pitfalls in the TypeScript FAQ.

Answer №2

For clearer error messages, it is recommended to structure your examples as follows:

const allProperties: InterfaceAllRequired = { a: 'a', b: 'b', c: 1 };
// No issues
const missingProperty: InterfaceAllRequired = { a: 'a', b: 'b' }
// Property 'c' is missing
const extraProperty: InterfaceAllRequired = { a: 'a', b: 'b', c: 3, additionalProp: 'fine' }
// Object literals can only include known properties
const extraAndMissingProperty: InterfaceAllRequired = { a: 'a', b: 'b', additionalProp: 'why?' };
// Object literals can only include known properties

Regarding why type assertions are successful for most cases but not the last one, it's important to understand TypeScript's structural typing:

{ a: 'a', b: 'b', c: 1 } is considered a subtype of { a: 'a', b: 'b' } because it contains all properties of the super type.

let x: { a: 'a', b: 'b', c: 1 };
let y: { a: 'a', b: 'b' };
x = y; // results in an error
y = x; // works fine

By using

x = y as { a: 'a', b: 'b', c: 1 }
, you essentially downcast y, resolving the error.

In the third example where

{ a: 'a', b: 'b', c: 3, extraProp: 'no-issues' }
is a subtype of InterfaceAllRequired, the type assertion succeeds. However, in the fourth case with
{ a: 'a', b: 'b', extraProp: 'what?' }
and InterfaceAllRequired being unrelated types, TypeScript prohibits downcasting in such scenarios. This scenario is analogous to:

let x: string;
let y: number;

x = y as string; // Cannot convert type 'number' to 'string'

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

Steps for clicking on the center of a leaflet map with protractor

I'm currently facing an issue where I am attempting to click on the center of a map located in the second column of the webpage, which has an offset. However, I am encountering a problem where the cursor always points to the center of the page instead ...

Encountering Issues with TypeScript Strict in Visual Studio Code Problems Panel

I have discovered that I can optimize my TypeScript compilation process by utilizing the --strict flag, which enhances type checking and more. Typically, I compile my TypeScript code directly from Visual Studio Code with a specific task that displays the c ...

There is a potential for the object to be 'undefined' when calling the getItem method on the window's local storage

if (window?.sessionStorage?.getItem('accessToken')?.length > 0) { this.navigateToApplication(); } Encountering the following error: Object is possibly 'undefined'.ts(2532) Any suggestions on how to resolve this issue? I am attem ...

Refresh the array using Composition API

Currently, I am working on a project that utilizes Vue along with Pinia store. export default { setup() { let rows: Row[] = store.history.rows; } } Everything is functioning properly at the moment, but there is a specific scenario where I need to ...

Verify the legitimacy of the object

I'm encountering an issue while attempting to create a function that verifies the validity of an object. const isPeriodValid = (period: Period | null): boolean => { return period && period.start && period.end; } export interfac ...

Does Angular 2/4 actually distinguish between cases?

I have a question about Angular and its case sensitivity. I encountered an issue with my code recently where using ngfor instead of ngFor caused it to not work properly. After fixing this, everything functioned as expected. Another discrepancy I noticed w ...

A guide to implementing typescript with Next.js getStaticProps

I have Next.js set up with the TypeScript feature enabled Currently, I am attempting to utilize the getStaticProps function following the guidelines outlined here: https://nextjs.org/docs/basic-features/typescript Utilizing the GetStaticProps type export ...

In order to successfully run this generator XXX, you need to have a minimum of version 4.0.0-rc.0 of yeoman-environment. However, the current version available

When attempting to run a generator using the latest version of yeoman-generator (7.1.0), yo discord An error message pops up saying: This generator (discord:app) requires yeoman-environment version 4.0.0-rc.0 or higher, but the current version is 3.19.3. ...

RxJs will only consider the initial occurrence of a specific type of value and ignore any subsequent occurrences until a different type of value is encountered

I'm faced with a situation where I need to extract the first occurrence of a specific value type, followed by the next unique value of a different type. Let's break it down with an example: of(1,1,1,1,2,3,4) .pipe( // some operators ) .subsc ...

Exploring the relationships between nested tuple types

When exploring the concept of mapped tuple types in TypeScript, the documentation provides an example: type MapToPromise<T> = { [K in keyof T]: Promise<T[K]> }; type Coordinate = [number, number] type PromiseCoordinate = MapToPromise<Coor ...

Are you encountering issues with Google Analytics performance on your Aurelia TypeScript project?

I recently started using Google Analytics and I am looking to integrate it into a website that I'm currently building. Current scenario Initially, I added the Google Analytics tracking code to my index.ejs file. Here is how the code looks: <!DOC ...

React component stuck in endless loop due to Intersection Observer

My goal is to track the visibility of 3 elements and update state each time one of them becomes visible. Despite trying various methods like other libraries, useMemo, useCallback, refs, etc., I still face challenges with my latest code: Endless loop scenar ...

Can Typescript Be Integrated into an AngularJS Application?

I have been thinking about the optimal timing and scenario to implement Typescript in an AngularJS project. While I have come across examples of TS being used in a Node, Express, Mongo backend, I am particularly intrigued by how well TS integrates with A ...

Template for typed variable - `ng-template`

How can the parent component correctly identify the type of let-content that is coming from ngTemplateOutletContext? The current usage of {{content.type}} works as expected, but my IDE is showing: unresolved variable type Is there a way to specify the ...

Tips for resolving the error "Binding element has no default value and initializer provides no value in TypeScript"

I am currently in the process of converting a JavaScript Apollo GraphQL API project to TypeScript. During this migration, I encountered an error related to a user code block: var idArg: any Initializer provides no value for this binding element and the ...

The primary access modifier used in C++

In my C++ code, I have an interface defined in the following way: // A.h #pragma once class A { public: //Some declarations. private: //Some declarations. protected: //Some declarations. }; While the exact syntax is not crucial, it&ap ...

Struggling with the @typescript-eslint/no-var-requires error when trying to include @axe-core/react? Here's a step-by

I have integrated axe-core/react into my project by: npm install --save-dev @axe-core/react Now, to make it work, I included the following code snippet in my index.tsx file: if (process.env.NODE_ENV !== 'production') { const axe = require(&a ...

What is the benefit of utilizing ngSubmit over just using a basic button and function?

Lately, I've been pondering whether to utilize ngSubmit or simply bind a (click)="submit()" on a button. There's been much debate about using submit and ngSubmit, but is it necessary to rely on the traditional HTML submit method? Particularly wh ...

What is the best way to reload a React/TypeScript page after submitting a POST request?

I am working on a custom plugin for Backstage that interacts with Argo CD via API calls. To retrieve application information, I make a GET request to the following endpoint: https://argocd.acme.com/api/v1/applications/${app-name} If the synchronizati ...

What happens when Angular elements don't have an injector?

Exploring Angular's custom elements while steering clear of dependency injection is my current experiment. const MyElementElement = createCustomElement(MyElementComponent, { injector }); customElements.define('my-element', MyElementElement) ...