Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typing anonymous objects in Typescript without using type assertions

Tags:

typescript

Consider:

type Foo = {
   bar: string,
   baz: number
}

const bars = ['a', 'b', 'c'];
const foos = bars.map<Foo>((bar, i) => {
   return {
     bar,
     baz: i
   }
});

This this will not enforce the exact type of the return value from the map, e.g. I can say

return {
  bar,
  baz: i,
  extraProp: 'boo!'    // this works! (bad - i want this to fail)
}

and it will work fine. This works the same as if I didn't use a generic type and used a type assertion on the return value:

return {
  bar,
  baz: i,
  extraProp: 'boo!'   // works (bad)
} as Foo

The only way I can figure out to get the actual type to be enforced completely is creating a temporary variable:

const returnVal: Foo = {
  bar,
  baz: i,
  // extraProp: 'boo!' // won't work - good! typescript prevented a bug!
}
return returnVal;

Is there any syntax that permits creating an anonymous object such as in the return statement that would allow enforcement of the type completely, rather than just a type assertion?

like image 224
Jamie Treworgy Avatar asked Mar 01 '18 14:03

Jamie Treworgy


People also ask

Should you type everything in TypeScript?

Yes, you should make it a habit to explicitly set all types, it's will prevent having unexpected values and also good for readability.

What does ?: Mean in TypeScript?

What does ?: mean in TypeScript? Using a question mark followed by a colon ( ?: ) means a property is optional. That said, a property can either have a value based on the type defined or its value can be undefined .

What keyword do you use to declare an anonymous type?

The anonymous type declaration starts with the new keyword. The declaration initializes a new type that uses only two properties from Product . Using anonymous types causes a smaller amount of data to be returned in the query.

How do you pass an object as a parameter in TypeScript?

Inside the function we assign the parameters to properties in the object. To do this, we have to specify the this keyword, which refers to the calling object. The variables and parameters may have the same names. Anything we pass to the constructor as an argument, will be assigned to the property of the object.


1 Answers

The problem is that object literals are checked for extra properties only when they are directly assigned to a parameter/variable/return value. While you do specify the type parameter on map the arrow function will first be typed first with this signature:

(bar: string , i: number) => {    
    bar: string,
    baz: number,
    extraProp: string
}

And then this function is checked for compatibility with the argument of map (typed as (bar: string , i: number) => Foo) and will be found to be compatible.

A simple work around would be to specify the type not on map but the return type of the arrow function :

const foos = bars.map((bar, i): Foo => {
    return {
        bar,
        baz: i,
        extra: "" // error here, as expected
    }
});

This is not a type assertion, but you do need to specify the type, but you do so on map anyway, so the amount of typing is about the same.

like image 177
Titian Cernicova-Dragomir Avatar answered Sep 30 '22 21:09

Titian Cernicova-Dragomir