How is it that in TypeScript, a potential numeric value in an interface can be transformed into an impossible numeric value in a class implementation?

Encountered a surprising behavior from the TypeScript compiler today. Unsure if it's a bug or intentional feature. If it is indeed intentional, I would like to understand the reasoning behind it.

The issue arises when declaring an interface method with a parameter that can be either a string or a number, and then implementing this interface in a class where the method only accepts a string. Despite this mismatch, the compiler allows a number to be passed as an argument without any errors being thrown. Why does the compiler permit this?

interface Foo {
    hello(value: string | number): void
}

class FooClass implements Foo {
    hello(value: string) { // Notice that 'number' is omitted
        console.log(`hello ${value}`)
    }
}

const x = new FooClass()

x.hello("me")

// x.hello(42) // This line will result in a compile error

const y: Foo = x

y.hello(42)

Answer №1

It's an interesting fact about TypeScript that it is not completely type-safe. In certain areas, intentionally unsound features were integrated to prevent hindrances to productivity. Check out the "a note on soundness" in the TypeScript Handbook for more insight. You've encountered one such feature related to method parameter bivariance.

When dealing with a function or method type that accepts a parameter of type A, the type safe implementation or extension should involve accepting a parameter of a supertype B of

A</code. This concept known as <a href="https://www.stephanboyer.com/post/132/what-are-covariance-and-contravariance" rel="noreferrer"><em>contravariance</em></a>. Therefore, if <code>A
extends B, then
((param: B) => void) extends ((param: A) => void)
. The relationship between subtypes for functions is the opposite of the subtype relationship for their parameters. Hence, having
{ hello(value: string | number): void }
would safely align with implementations like
{ hello(value: string | number | boolean): void }
or { hello(value: unknown): void}.

However, implementing it with { hello(value: string): void}; implies that the implementation is accepting a subtype of the declared parameter, which highlights covariance - an unsafe practice. TypeScript allows both the safe contravariant and unsafe covariant implementations due to bivariance.

The permission of method parameter covariance stemmed from commonly used types having covariant method parameters. Enforcing contravariance could disrupt these types' formation within a subtype hierarchy. For instance, consider the example concerning Array<T> in the FAQ entry on parameter bivariance.


Prior to TypeScript 2.6, all functions adhered to this behavior. Subsequently, the introduction of the --strictFunctionTypes compiler flag was implemented. Upon enabling this flag (which is recommended), function parameter types are checked covariantly (safe), while method parameter types continue to observe bivariant checks (unsafe).

The distinction between a function and a method in the type system reflects subtle differences. Both types are essentially identical, except that for the former, a serves as a method, whereas in the latter, a functions as a function-valued property. Consequently, parameter checking for the two types differs. Despite this variance, they behave similarly in many aspects, allowing interchangeable implementation choices.

To address the issue at hand, consider the following potential solution:

interface Foo {
    hello: (value: string | number) => void 
}

By structuring hello as a function rather than a method type, discrepancies may be highlighted within the class implementation, thereby signaling errors that need attention and correction.

Ensuing implementations can eradicate initial errors when aligned appropriately with supertypes of the designated parameter.


Henceforth, taking necessary precautions facilitates error detection and rectification. Ensure to utilize the provided Playground link to further explore code implementation.

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

I aim to link a variable in a directive with a component

Each directive comes with its own functionality and specific features. It can be challenging to understand how to connect a variable from a directive to a component. This particular directive involves detecting x-axis and y-axis positions during mouse ev ...

Utilizing Vue 3 props validation in conjunction with the power of Typescript

Looking to customize a Link component using Nuxt, Typescript, and the composition-api. The prop target can accept specific values as outlined below. I'm curious if using a custom validator function to check prop types at runtime adds value when compar ...

Specialized type for extra restriction on enum matching

In my current project, I am dealing with two enums named SourceEnum and TargetEnum. Each enum has a corresponding function that is called with specific parameters based on the enum value. The expected parameter types are defined by the type mappings Source ...

Creating a function within a module that takes in a relative file path in NodeJs

Currently, I am working on creating a function similar to NodeJS require. With this function, you can call require("./your-file") and the file ./your-file will be understood as a sibling of the calling module, eliminating the need to specify the full path. ...

Is TypeScript to blame for the unexpected token error in Nock?

My code snippet in the ts file looks like this: nock('https://example.test').post('/submit').reply(200,{ "status": "Invalid", "message": "Invalid Request", }); However, when I try to ...

Creating interactive forms - Incorporating dynamic checkbox options within the expansion panel element

Recently, I developed a basic movie list app with a checkbox list for genre filtering. Initially, I managed to achieve the desired functionality without using reactive forms. However, I am now exploring implementing the same functionality using reactive ...

Encountering a TypeScript error in Next.js: The 'Options' type does not align with the 'NavigateOptions' type

My code snippet: import { useRouter } from 'next/navigation'; interface Options { scroll: boolean; } const Component = () => { const router = useRouter(); const updateSearchParams = () => { const searchParams = new URLSearchPa ...

When the component I created for dark mode is clicked, the colors in react-tsparticles change dynamically

I am looking to modify the theme of the react-tsparticle component function Particle() { const particlesInit = (main) => {}; const particlesLoaded = (container) => { <DarkMode />; container.loadTheme("dark&q ...

Deep Dive into TypeScript String Literal Types

Trying to find a solution for implementing TSDocs with a string literal type declaration in TypeScript. For instance: type InputType = /** Comment should also appear for num1 */ 'num1' | /** Would like the TSDoc to be visible for num2 as well ...

What could be causing my "Swiper" component to malfunction in a TypeScript React project?

In my React project, I decided to incorporate the Swiper library. With multiple movie elements that I want to swipe through, I began by importing it as follows: import Swiper from 'react-id-swiper'; Utilizing it in my code like this: <div cla ...

Angular 5 is rendering a div even if there is no content present

I am currently using Angular 5.2 Firestore When using *ngIf isContent else noContent, my goal is to only render an Observable if it contains data. However, I am facing an issue where the logic always renders isContent even when there is no data present. ...

Error: Unable to access null properties while attempting to address Readonly property error by implementing an interface

Here is the code snippet I am working with: interface State { backgroundColor: boolean; isLoading: boolean; errorOccured: boolean; acknowledgment: string; } export class GoodIntention extends React.Component<Props, State> { ... onCli ...

Angular form requests can utilize the Spring Security Jwt Token to allow all options methods

Despite checking numerous sources online, I am facing a persistent issue with my Angular application. The problem arises when using HttpClient along with an Angular interceptor to set headers for authentication in my Java Rest API using JWT tokens. The int ...

Ways to determine if a specified character sequence is present in an Enumerator?

One of my coding dilemmas involves an enum that looks like this: export enum someEnum { None = <any>'', value1 = <any>'value1', value2 = <any>'value2', value3 = <any>'value3' ...

What could be causing the issue with my customized sorting in the Material-UI Data Grid component?

After integrating Material-UI's Data Grid Component with my API's JSON array, I had to create a RenderCell function to handle text overflow and include a button that directs Users to a different page. Additionally, I utilized the ValueGetter for ...

Leverage generic types and allow acceptance of objects with arbitrary keys

Is it possible to allow the Use function argument type to accept any unknown key, as well as correctly type the keys from SomeGeneric? function Example (opt: { valid?: boolean }) { } type SomeGeneric = Parameters<typeof Example>[0] function Use(op ...

Ways to solve VScode gutter indicator glitches after switching text editors?

When my active function is running, I have a specific updateTrigger that ensures certain actions are taken when the activeTextEditor in vscode changes: const updateTrigger = () => { if (vscode.window.activeTextEditor) { updateDecorations(con ...

Set up a TypeScript project with essential dependencies for creating multiple raw JavaScript output files

Currently, I am in the process of developing scripts for Bot Land, a real-time strategy game that offers a unique gameplay experience. Rather than controlling units traditionally with a mouse and keyboard, players code their bots using an API to engage in ...

Utilizing the onBlur event to control focus within a React element

In the React component I'm working on, I have implemented an onBlur event handler. The logic inside this handler is supposed to return focus back to the target element. This code is written in TypeScript. emailBlur(e: React.FocusEvent<HTMLInputEle ...

Ways to extract values from a javascript hash map by exclusively incorporating an array

So here's the issue I'm encountering. Let's consider the following scenario: let surfaces: Map<any, any> = new Map([{"83.1" => Object}, {"84.1" => Object}]) let arr1 = ["83.1"] This is the desired o ...