Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript function accepting incorrect argument with no error

Tags:

typescript

Case 1: No errors detected when "randomKey" is passed down to doMethod.

interface Options {
 bar: string;
 foo?: number;
}

function doMethod(options: Options) {
 console.log(options);
}


const options = {
 bar: 'bar',
 foo: 123,
 randomKey: 231,
}
doMethod(options); // No errors appear!
console.log(options);

Case 2: Errors only appears after when options are directly passed into the function

interface Options {
 bar: string;
 foo?: number;
}

function doMethod(options: Options) {
 console.log(options);
}

doMethod({
 bar: 'bar',
 foo: 123,
 randomKey: 231, // ERROR: "Object literal may only specify known properties, and 'randomKey' does not exist in type 'Options'."
});

console.log(options); // In this scenario, I cannot console log options

The silent error is tough to find. You can easily misspell a word for example, and things might not happen they way you want it.

What happens when you misspell "foo", like "fooo"? The silent error would still occur. You expect "foo" to trigger something but would not in this case if you spelt "fooo".

like image 690
Richard Avatar asked Mar 16 '26 03:03

Richard


1 Answers

TypeScript provides a concept of Freshness (also called strict object literal checking) to make it easier to type check object literals that would otherwise be structurally type compatible.

-- Source TypeScript Deep Dive

Follow the link above, it's explains quite clear on this topic.

Here I'll explain briefly with your scenario.

// case 1: no error
const options = { bar: 'bar', foo: 123, randomKey: 231 };
doMethod(options);

// case 2: error
doMethod({ bar: 'bar', foo: 123, randomKey: 231 });

In runtime JavaScript, both use case should just work. That's true because JS is weak-typed, extra keys in an object cause no harm in runtime.

Back to TS, you specify that doMethod(options) shall accept options that has the shape { bar: string; foo?: number }. This is stating your minimum requirement for the input to doMethod. Input with extra keys? No problem, that's how JS works.

Then, why in the world would TS throw error on you in case 2?

Because TS want you to know that extra randomKey is unnecessary! Even though it does no harm.

This is the idea behind the freshness concept. An analog would be like:

function add(a: number, b: number) { return a + b };

add(1, 2, 3); // TS Error

Though in JS runtime, that usage still work, you probably don't mean that. So TS warns you about it ahead of time.


To answer the second part of your question, what if you just want to preserve the freshness of object, what if you want the exact shape of input to be strictly checked? There is a solution with drawback:

type ExactShape<T, R> = T extends R
  ? Required<R> extends T
    ? T
    : never
  : never

function doMethod<T>(options: ExactShape<T, Options>) {
   console.log(options);
}

TypeScript Playground Live Demo

This time TS will throw error like you wish. But the drawback is, when mouse hover on error, it will no longer tell useful info why you're wrong.

like image 52
hackape Avatar answered Mar 17 '26 18:03

hackape



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!