Aquarius_Girl
Aquarius_Girl

Reputation: 22906

Change the inference by adding a type assertion in Typescript

From: https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-inference

TypeScript doesn’t assume the assignment of 1 to a field which previously had 0 is an error. Another way of saying this is that obj.counter must have the type number, not 0, because types are used to determine both reading and writing behavior.

The same applies to strings:

const req = { url: "https://example.com", method: "GET" };
handleRequest(req.url, req.method);

// Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.

I thought "GET" is a default value assigned to variable 'method' of the object 'req'. What is the error saying?

  1. You can change the inference by adding a type assertion in either location:

    // Change 1:
    const req = { url: "https://example.com", method: "GET" as "GET" };
    // Change 2
    handleRequest(req.url, req.method as "GET");
    

    Change 1 means “I intend for req.method to always have the literal type "GET"”, preventing the possible assignment of "GUESS" to that field after. Change 2 means “I know for other reasons that req.method has the value "GET"“.

What do they mean by adding a type assertion in this case?

What is happening here?

Upvotes: 0

Views: 579

Answers (2)

jperl
jperl

Reputation: 5112

If you hover over req, you'll see that method is a string. The types have been "widened". You have to find a way to tell Typescript not to do that. One of these is using const.

const req = { url: "https://example.com", method: "GET" } as const;

Or you can explicitly say that method is either "GET" or "POST".

const req: { url: string, method: "GET" | "POST" } = { url: "https://example.com", method: "GET" as "GET" };

or using type assertion like you did as. (We are often told not to use type assertions but that's a good use case)

This would work as well

const req = { url: "https://example.com", method: "GET" as const }; // const just on method

This little example will help you understand better what's happening:

let num = 3 //number since you can assign something else to num
const num = 3 //3 since it's a const (not supposed to change)

Upvotes: 3

Giovanni Londero
Giovanni Londero

Reputation: 1409

When creating req, you're basically creating an object with an url: string and method: string. Here's an example equal to your code:

interface Request {
  url: string;
  method: string;
}
const req: Request = { url: "https://example.com", method: "GET" };
handleRequest(req.url, req.method);

but this way, you can't ensure that method is GET or POST, so if handleRequest expects one of those values it will complain if you pass a "string", which could equal to anything.

A solution could be to define a type for the req, for example:

interface Request {
  url: string;
  method: 'GET' | 'POST';
}

// or using 'type' if you prefer
// type Request = {
//   url: string;
//   method: 'GET' | 'POST';
// }

const req: Request = { url: "https://example.com", method: "GET" };
handleRequest(req.url, req.method);

Upvotes: 1

Related Questions