30

Demystifying a TypeScript quirk

 5 years ago
source link: https://matthewmiller.dev/blog/demystifying-typescript-quirk/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

I recently read Asana's blog post on TypeScript quirks and took particular interest in the first TypeScript quirk they mention. While it may seem like an inconsistency, the way the type system behaves here is entirely logical.

The article uses the interface Dog and function printDog as an example,

interface Dog {
  breed: string
} 

function printDog(dog: Dog) {
    console.log("Dog: " + dog.breed)
}

This code mimics a widespread pattern in TypeScript and acts as a base for typing of objects. So far, so good, but the article then introduces a case that could appear to be an inconsistency. While you can pass any object that contains a breed property to the function when it's assigned to a variable, you cannot pass it directly to the function.

const ginger = {
    breed: "Airedale",
    age: 3
};
printDog(ginger); //works

printDog({
    breed: "Airedale",
    age: 3
}); //fails

What's going on here? TypeScript checks for excessive properties in objects to catch bugs, but why does it only become an issue when the object is passed directly to the function?

As the article points out, TypeScript is a structurally typed language. This classification means that types are checked based on the object's structure rather than an inherent type on the object. There is an exception to this rule; however, which is when the developer explicitly types a variable. TypeScript functions parameters are also covariant, which means that they allow anything that extends the base type. In a structurally typed language, adding a property to an already known type would be seen as extending it.

So why does this error at all then? If it's ultimately allowed to pass in objects that are subtypes of the given type, why does it complain in some circumstances?

The answer comes back to the exception in the type system that I brought up earlier. When creating a variable and passing it to the function, the variable infers a type. When you create the object in the function call, you are explicitly telling TypeScript that the variable is of type Dog . Creating variables has an excessive type check, but function calls do not due to covariance. This "inconsistency" also has nothing to do with the function call. You can create the same behaviour just by providing a type when creating the object.

const ginger: Dog = {
    breed: "Airedale",
    age: 3
};

This code will provide the same error as the function call, as age is not a known property on the type Dog .

While issues like this can often confuse TypeScript developers, there's usually an explanation for any behaviour that the type system exhibits. If not, you should probably report it to Microsoft.


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK