Incorrect function assignment

I am encountering an issue in non-strict mode. Below is my code where I seem to be able to assign a function with an incorrect type to a typed variable.

Am I making a mistake?

Thank you.

interface A {
    f1( ) : void;
}

interface B extends A {
    f2( ) : void;
}

interface C {
    f3( ) : void;
}

type expectA = ( a: A ) => void;
type expectB = ( b: B ) => void;


function testA( a: A ) {
    console.log( "a" );
}

function testB( b: B ) {
    console.log( "b" );
}

function testC( c: C ) {
    console.log( "c" );
}


let v1: expectA = testA;    // Okay: assigning type A to type A
let v2: expectA = testB;    // Okay: assigning type B (which extends A) to type A
let v3: expectA = testC;    // Fails -> Expected behavior: error TS2322: Type '(c: C) => void' is not assignable to type 'expectA'.
let v4: expectB = testA;    // Okay -> **Unexpected behavior**: no error thrown in !strict mode

Answer №1

When it comes to TypeScript, it acts as a safety net against potential mistakes, although its approach may be puzzling at times.

To simplify, let's focus on the interfaces A and B.

interface A {
    f1( ) : void;
}

interface B extends A {
    f2( ) : void;
}

type expectA = ( a: A ) => void;
type expectB = ( b: B ) => void;


function testA( a: A ) {
    a.f1();
}

function testB( b: B ) {
    b.f1();
    b.f2();
}


const v1: expectA = testA;
const v2: expectA = testB; // error: Property 'f2' is missing in type 'A' but required in type 'B'
const v3: expectB = testB;
const v4: expectB = testA;

Initially, seeing that only v2 has an error might seem counterintuitive. If B extends A, why can't we use B everywhere A can be used?

The reason lies in the nature of functions. Take a closer look at testB(). It utilizes the property b.f2() assuming that its parameter b possesses that property. However, const v2: expectB represents the type (a: A) => void. An argument of type A does not have f2(), leading to conflicting information about the type of

v2</code and creating a paradoxical situation.</p>
<p>(Note that this discrepancy is unrelated to whether <code>testB
actually calls
b.f2()</code; the issue arises from the potentiality of such an action based on the argument types defined.)</p>
<p>Regarding <code>const v4
, although you consider it "abnormal", upon revisiting the functions, it becomes evident why it is acceptable. Passing either a variable of type A or B to testA() poses no risk since it never attempts to access the f2() property.

Furthermore, the usage of extends in TypeScript differs from common expectations. Declaring interface B extends A simply denotes that B inherits all properties of A, without implying a substitution relationship between instances of A with instances of

B</code. For true polymorphic behavior, classes must be employed, like so: <code>class B extends A implements A
.

class A {
    foo = '';
}

class B extends A implements A {
    bar = '';
}

let isA = new A();
isA = new B(); // valid due to B implementing A
let isB = new B();
isB = new A(); // error: Property 'bar' is missing in type 'A' but required in type 'B'

Answer №2

Thank you for your reply, but I actually was referring to the difference between strict and non-strict modes in TypeScript. (My initial description had a slight error, my apologies for that)

When mentioning non-strict mode, I meant using TypeScript without the "strict" option enabled in tsconfig.json.

"strict": true, /* Enable all strict type-checking options. */

Your simplified version of the code is:

interface A {
    f1( ) : void;
}

interface B extends A {
    f2( ) : void;
}

type expectA = ( a: A ) => void;
  
function testB( b: B ) {
    console.log( "b" );
    b.f1();
    b.f2();
}
    
// In strict mode, there will be an error which is expected
// However, in non-strict mode, no error occurs
let v2: expectA = testB;    // error TS2322: Type '(b: B) => void' is not assignable to type 'expectA'.

class ClassB implements B {
    f1( ) {}
    f2( ) {}
}

let t = new ClassB( );
v2( t );

I find it curious why the line below is valid in non-strict mode. It works because of the relationship between the B interface and the A interface. If this inheritance is removed, the compiler raises an error.

let v2: expectA = testB

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

Trigger Angular2 EventEmitter in child component to inform parent component

I am having trouble triggering an event from the child component to the parent component. @Component({ template:'<foo></foo>' }) export class ParentComponent{ onDoSomething($event){ //handling logic goes here } } @Compo ...

Is there a recommended approach in Vuex to detect when a state change has occurred?

Our team has developed a Vuex module to store user-input data from a form. If the form has unsaved changes and the user tries to leave the page, we want to warn them so they can decide whether or not to proceed. It would be convenient if we could simply u ...

Does moment/moment-timezone have a feature that allows for the conversion of a timezone name into a more easily comprehendible format?

Consider this example project where a timezone name needs to be converted to a more readable format. For instance: input: America/Los_Angeles output: America Los Angeles While "America/Los_Angeles" may seem human-readable, the requirement is to convert ...

I require a duplicate of the original data. Direct references are unnecessary

I am trying to extract the value of data.notes. After implementing the code below, I noticed that the detailsOfCurrentNotes value changes based on the data.notes. Can anyone provide guidance on how to resolve this issue? notes :Note[] const detailsOfCur ...

Can Bun automatically bundle my TypeScript files when I save them in VS Code?

Is it feasible for Bun to bundle my TypeScript upon saving a file in VS Code? The instruction manual suggests running bun run index.ts in the command line and including it in the package.json in this manner. However, I am unsure how to automate this proce ...

Difficulty with setting up Typescript in Visual Studio Code on MacOS Catalina

I'm currently facing an issue that appears to be related to the environment. How can I resolve it? And how do I link the installed TSC to my console? Steps to Recreate: npm install -g typescript was able to successfully install or update [email ...

Tips on preventing image previews from consuming too much text data while updating a database in Angular 12 using Material UI for image uploads within a FormGroup object

Currently working with Angular 12 and Angular Material for image uploads with preview. I have a formgroup object below, but I'm running into issues with the 197kb image preview text being inserted into the database. Despite trying setValue/patchValue/ ...

What is the best way to effectively personalize my Bootstrap variables using SASS?

After creating a web page and adding Bootstrap styling, I attempted to customize the Bootstrap variables, but encountered an issue where it did not seem to work despite no errors being displayed. I followed a tutorial on YouTube meticulously, but to no av ...

Dynamically apply classes in Angular using ngClass

Help needed with setting a class dynamically. Any guidance is appreciated. Below is the class in my SCSS file: .form-validation.invalid { border: 2px solid red } In my ts file, there's a variable named isEmailValid. When this variable is set to ...

TypeScript is unable to identify the data type of class members

I am working with a class called Colors: export class Colors { constructor( private domainColors: string[] = ['#F44336', '#FDB856', '#59CA08', '#08821C'], private numberRange: [number | string, number | string] = [-1 ...

Trouble arises when extending an MUI component due to a TypeScript error indicating a missing 'css' property

We have enhanced the SnackbarContent component by creating our own custom one called MySnackbarContent: export interface MySnackbarContentProps extends Omit<SnackbarContentProps, 'variant'> { variant?: MyCustomVariant; type?: MyCustomTy ...

There are no matching overloads in React for this call

Below is an error message in the code. It seems to be related to the usage of <IHistorical[]> in useQuery, but unfortunately, I haven't found a solution for it yet. Overload 1 of 2, '(props: Props | Readonly<Props>): ReactApexChart& ...

Sharing properties between components

While this topic has been discussed extensively, I am still struggling with my specific example. In my setup, I have a react-select component nested within another component, which is then part of the larger App component. SubjectSelect.tsx export default ...

Is there a way to correct Typescript's misunderstanding of the interface from a JSON file that has been imported?

The structure of my JSON file is as follows: [ { "keys": [ { "date": "2019-06-25T17:33:39.000Z" } ], "tag": null }, { "keys": [], "tag": "stringvalue" } ] Upon importing the file, Typescript assumes that ke ...

Tips for organizing MUI Card effectively within a React application using TypeScript

Struggling to build a React card component with Material-UI (MUI) and TypeScript that represents a car? The card should contain text, an image, checkboxes, a rating, and a button. Here's the code I've come up with so far: import React from ' ...

Angular 10: handling undefined error even with if statement checking for null values

After fetching a product from the backend, I ensure that if it contains images, they are also loaded. This functionality is already functioning properly when images are present. However, I need to implement a conditional check to skip products without imag ...

Angular is reporting that the check-in component is nonexistent

I encountered an error in my Angular 8 application while working on a component. The error seems to be related to nested components within the main component. It appears that if the component is empty, the error will be shown, but if it's not null, th ...

Utilizing an array of options within Typescript to define a custom prop type

Is there a way to validate a prop based on the values of another prop in TypeScript? The possible options for an array can be dynamic, making it impossible to manually define them. How can we enforce a selected prop to match one of the options provided? / ...

Creating an interactive element in Angular: A step-by-step guide

Hey there, I'm facing a problem with creating a specific payload structure as shown in the example code snippet below. Everything was going well until I got stuck at the dynamic form part, and despite several attempts, I can't seem to figure it o ...

Error: BrowserModule has already been loaded

After updating my application to RC6, I encountered a persistent error message: zone.js:484 Unhandled Promise rejection: BrowserModule has already been loaded. If you need access to common directives like NgIf and NgFor from a lazily loaded module.. ...