DEV Community

David Johnston
David Johnston

Posted on

1

Type widening on strings

The Problem

type Roles = "student" | "teacher"; 

type Person = {
    role: Roles; 
    name: string; 
    age: number; 
}

function doSomethingWithPerson(person: Person) {
    console.log(person); 
}

const andy = {
    role: "teacher", 
    name: "andy", 
    age: 31
};

doSomethingWithPerson(andy); //Argument of type '{ role: string; name: string; age: number; }' is not assignable to parameter of type 'Person'.

Playground Link

This code produces the error:

Argument of type '{ role: string; name: string; age: number; }' is not assignable to parameter of type 'Person'.
  Types of property 'role' are incompatible.
    Type 'string' is not assignable to type 'Roles'.

What's going on here?

The andy object we declare does match the type Person that the function wants, so why is TypeScript complaining?

Quick Solution

Declare the string "teacher" as const

const andy = {
    role: "teacher" as const, 
    name: "andy", 
    age: 31
};

doSomethingWithPerson(andy);

Explanation

In this scenario when we declare the object andy TypeScript infers the type of the role property as string, any string!

While "teacher" and "student" are strings, they are a subset of strings.

The Person type doesn't want any string, it wants just those two particular strings.

That TypeScript infers the type as string and not as "teacher" is called type widening.

What TypeScript is doing, is allowing for that you might later write some code like:

   andy.role = "foobar"; 

(That is, you're mutating the andy object).

What the as const keyword does is just prevent the type widening. See the documentation here.

What you're also doing is telling TypeScript 'don't worry, I promise I'm not going to reassign this property', and if you try, TypeScript will give you an error:

const andy = {
    role: "teacher" as const, 
    name: "andy", 
    age: 31
};

andy.role = "foobar"; //Type '"foobar"' is not assignable to type '"teacher"'.(2322) 

Alternative solutions

Declare the string "teacher" as "teacher"

const andy = {
    role: "teacher" as "teacher", 
    name: "andy", 
    age: 31
};

doSomethingWithPerson(andy);

What this does is instead of using the as const keyword to prevent type widening, it just explicitly sets the type of the role property to "teacher".

Both as const and as "teacher" have the same end effect, the role property's type is "teacher".

AWS Q Developer image

Your AI Code Assistant

Generate and update README files, create data-flow diagrams, and keep your project fully documented. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay