Reputation: 57
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".
Upvotes: 3
Views: 657
Reputation: 19947
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.
Upvotes: 4
Reputation: 383
There's no problem. It's normal behavior. Think about the interface as promise to provide the data.
It's important to remember that typescript at the end of the day is compiled to vanilla javascipt.
As long as you provide all data for the interface it doesn't matter if the actual object you provide contains extra information.
Good example is
interface Options {
bar: string;
foo?: number;
randomKey: number
}
const doMethod =(options: Options): any=> {
console.log(options);
}
const options = {
bar: 'bar',
foo: 123
}
doMethod(options); ** error appears!**
console.log(options);
To get better typesafe experience you might want to use interface for the object as well.
const options: Options = { bar: 'bar', foo: 123 }
That way you will be forced to comply with interface. And get error in case you try to add unexpected extra data
Upvotes: -1