cf.completefrontend
Open lab My progress
THE TYPESCRIPT FIELD GUIDE

Structural typing

Learn why TypeScript compares object shapes, not type names: any object with the right properties fits, extras are fine, and two names can mean one type.

By the end, you can
  • 01
    Predict when an object fitsCheck a value against a type property by property: every property the type lists must be there, with a type that fits.
  • 02
    Read assignability errorsExplain messages like Property 'price' is missing and Types of property 'price' are incompatible in plain words.
  • 03
    Spot where names don't matterExplain why two types with the same shape are interchangeable, and keep look-alike values such as IDs apart.

An object fits if it has the right properties

In JavaScript, a function that reads user.name works with any object that has a name. It doesn't care what else the object holds, or where it came from:

A greeting that only needs a namePop out in the code editor (opens in a new tab)JavaScript
function greet(user) {
  return `Hi, ${user.name}`;
}

console.log(greet({ name: "Asha", email: "asha@example.com" }));
console.log(greet({ name: "Ravi" }));

Lines 1 to 3 make a greet function that reads user.name. Line 5 passes an object with a name and an email, and prints Hi, Asha. Line 6 passes an object with only a name, and prints Hi, Ravi. Both work, because both objects have a name (Object basics covers objects and properties).

TypeScript checks this the same way. Here is the function with a type for its parameter:

An object with extra properties fits UserPop out in the code editor (opens in a new tab)TypeScript
type User = { name: string }; function greet(user: User) {  return `Hi, ${user.name}`;} const asha = { name: "Asha", email: "asha@example.com", cartItems: 2 };//    ^?console.log(greet(asha));

Line 1 is a type alias: type User = … gives the name User to an object type, a list of the properties an object must have and the type of each (the Object types lesson covers writing them). Here the list has one entry, a name that is a string (text). Line 3 says greet takes a User.

Line 7 makes asha without saying it's a User. The query on line 8 shows the type TypeScript worked out from the value: an object with a name, an email, and a number of cartItems. Line 9 passes it to greet. There are no errors, and it prints Hi, Asha.

TypeScript never asked whether asha was declared as a User. It asked one question: does asha have a name that is text? It does, so it fits. The extra email and cartItems don't matter.

Left, the object asha with three properties: name "Asha", email "asha@example.com", and cartItems 2. Right, the type User, which needs only name: string. An arrow joins the two name cards, marked matches, and the note says it fits.ashaname: "Asha"email: "asha@example.com"cartItems: 2the objectmatchesit fitsUsername: stringneeds only a namethe type
TypeScript only looks for what User lists: a name that is text. asha has one, so it fits. Its other two properties don't matter.

This way of checking is called structural typing. The "structure", or shape, of a type is the list of its property names and their types. TypeScript compares shapes, not names. You may also hear it called duck typing, from the saying "if it walks like a duck and quacks like a duck, it's a duck".

Real-life analogyA delivery job that asks for a licence and a phone

A shop needs someone to deliver parcels. The advert asks for two things: a driving licence and a phone. A student, a teacher, or a retired chef can all apply. The shop checks the licence and the phone, and nothing else.

In real life: The job asks for a driving licence and a phone
In TypeScript: The type lists name and price
In real life: Anyone who has both can take the job
In TypeScript: Any object with both properties fits
In real life: Being a good cook as well doesn't hurt
In TypeScript: Extra properties are fine
In real life: Nobody asks what your last job title was
In TypeScript: TypeScript doesn't compare type names
The main idea

TypeScript compares shapes, not names. A value fits a type when it has every property the type lists, each with a type that fits. It doesn't matter what the value's type is called, how it was made, or what extra properties it has.

Shapes, not names

A type's name is only a label for its shape. So two types with different names and the same properties are the same type to TypeScript:

A Customer fits where a Subscriber goesPop out in the code editor (opens in a new tab)TypeScript
type Customer = { name: string; email: string };type Subscriber = { name: string; email: string }; function sendNewsletter(subscriber: Subscriber) {  return `Sending news to ${subscriber.email}`;} const asha: Customer = { name: "Asha", email: "asha@example.com" };console.log(sendNewsletter(asha));

Lines 1 and 2 make two types, Customer and Subscriber, each with a name and an email. Line 4 says sendNewsletter takes a Subscriber. Line 8 makes asha a Customer. Line 9 passes the customer where a subscriber goes. There are no errors, and it prints Sending news to asha@example.com.

Two name tags, Customer and Subscriber, both point at the same card: { name: string; email: string }. Two names, one shape.two namesCustomerSubscriber{ name: string; email: string }one shape
Customer and Subscriber are two names for the same shape. TypeScript compares the shape, so a Customer fits wherever a Subscriber goes.

An interface is another way to name an object type. You write interface Customer { … } with the properties inside, instead of type Customer = { … }. The Type aliases & interfaces lesson compares the two. For this lesson, the point is that TypeScript compares interfaces by shape too:

An interface and a type alias with the same shapePop out in the code editor (opens in a new tab)TypeScript
interface Customer {  name: string;  email: string;} type Contact = { name: string; email: string }; const asha: Customer = { name: "Asha", email: "asha@example.com" };const contact: Contact = asha;//    ^?console.log(contact.email);

Lines 1 to 4 make an interface called Customer. Line 6 makes a type alias called Contact with the same two properties. Line 8 makes a Customer, and line 9 stores it in a Contact variable. The query on line 10 shows const contact: Contact, there are no errors, and line 11 prints asha@example.com.

When one type fits another

When a value fits a type, TypeScript says it is assignable to that type: allowed to be stored in a variable of that type, or passed to a parameter of that type. To decide, TypeScript goes through the type's list, one property at a time:

  1. Does the value have a property with this name? If not, the property is missing.
  2. Does that property's type fit the type the list says? If not, the two are incompatible.
  3. Once every property on the list is checked, anything else on the value is ignored.

Try it. The code below has a CartItem type with a name and a price, and an addToCart function that takes one. Type the inside of the cake object on line 7, and watch what TypeScript says about the call on line 8:

Playground: build the cake object
A cake for the cartPop out in the code editor (opens in a new tab)TypeScript
type CartItem = { name: string; price: number }; function addToCart(item: CartItem) {  console.log(`Added ${item.name} for $${item.price}`);} const cake = { name: "Cake", price: 4 };addToCart(cake);
Line 7 holds{ name: "Cake", price: 4 }
What happens
TypeScriptNo errors
When it runsAdded Cake for $4
Try it yourself

The object has a name that is text and a price that is a number, which is everything CartItem lists. No errors, and the cart says Added Cake for $4.

The code is checked by the real TypeScript compiler each time you type. Only line 7 changes. Reset puts the name and price back.

Each suggestion tests one step of the list. Remove the price, and the call gets an error. The message has two lines. The first, Argument of type '{ name: string; }' is not assignable to parameter of type 'CartItem', means "this value doesn't fit the parameter". The second line says why: Property 'price' is missing. With price: "4", the second line becomes Types of property 'price' are incompatible: the price is there, but its type is wrong. Renaming name to title makes name missing, because names have to match exactly. An extra inStock, or the same properties in a different order, change nothing.

The table below sums up the tries. Read each row as: this cake object, does it fit CartItem, and why.

Which cake objects fit CartItem
The objectDoes it fit?Why
{ name: "Cake", price: 4 }Yes.It has every property CartItem lists, each with the right type.
{ price: 4, name: "Cake" }Yes.The order of properties doesn't matter. TypeScript looks each one up by name.
{ name: "Cake", price: 4, inStock: true }Yes, from a variable.Extra properties don't matter. Only what CartItem lists is checked.
{ name: "Cake" }No.The price that CartItem needs is missing.
{ name: "Cake", price: "4" }No.The price is there, but it's text, and CartItem says a number.
{ title: "Cake", price: 4 }No.title is a different name, so name is missing.

The check has a direction. A Product with a name, a price, and a stock count fits where a CartItem goes, because it has everything a cart item needs. A CartItem doesn't fit where a Product goes, because it has no stock. Press each button to change line 7, and compare what the type needs with what the value has:

Playground: does it fit?
A product, a cart item, and one assignmentPop out in the code editor (opens in a new tab)TypeScript
type Product = { name: string; price: number; stock: number };type CartItem = { name: string; price: number }; const tea: Product = { name: "Tea", price: 3, stock: 9 };const cake: CartItem = { name: "Cake", price: 4 }; const item: CartItem = tea;
TypeScriptNo errors
Property by property
The type needsname, price
The value hasname, price, stock
Try it yourself

CartItem needs a name and a price. tea has both, plus a stock that CartItem doesn't mention. Extra properties don't matter, so it fits.

TypeScript checks the code for real each time. Each button changes only line 7. Reset goes back to tea into CartItem.

tea into CartItem and tea into { name: string } fit: the type asks for less than tea has. cake into Product gets Property 'stock' is missing in type 'CartItem' but required in type 'Product'. tea into { price: string } gets Types of property 'price' are incompatible, because the price is a number and the type wants text. tea into { name: string } also shows that a type doesn't need a name at all: an object type written in place is compared the same way.

Top row: tea, with name, price, and stock, goes into CartItem, which needs name and price. It fits. Bottom row: cake, with name and price, goes into Product, which needs name, price, and stock. It has no stock, so it doesn't fit.the valuethe type it goes intoteanamepricestockfitsCartItemnamepricecakenamepriceno stockProductnamepricestock
Top: tea has everything CartItem needs, plus a stock, so it fits. Bottom: Product needs a stock that cake doesn't have, so line 7 gets an error.

Extra properties are fine

Extra properties being fine is what makes structural typing useful in frontend code. Data from a server usually has more fields than any one function needs:

A function that needs two fields of a big replyPop out in the code editor (opens in a new tab)TypeScript
type AvatarUser = { name: string; avatarUrl: string }; function avatarAlt(user: AvatarUser) {  return `Photo of ${user.name}`;} const reply = {  id: 42,  name: "Asha",  email: "asha@example.com",  avatarUrl: "/avatars/asha.png",  joined: "2024-03-01",};console.log(avatarAlt(reply));

Line 1 says an AvatarUser has a name and an avatar address. Line 3 says avatarAlt takes one. Lines 7 to 13 are a user as a server might send it, with five fields. Line 14 passes the whole reply. It has the two fields AvatarUser needs, so there are no errors, and it prints Photo of Asha. You don't have to copy the two fields into a new object first.

The check goes inside nested objects too, with the same rule at every level:

A nested customer with an extra emailPop out in the code editor (opens in a new tab)TypeScript
type Order = { id: number; customer: { name: string } }; const reply = { id: 7, customer: { name: "Asha", email: "asha@example.com" }, total: 12 };const order: Order = reply;//    ^?console.log(order.customer.name);

Line 1 says an Order has an id and a customer, and the customer has a name. Line 3's reply has an extra total, and its customer has an extra email. Line 4 stores it as an Order, and the query on line 5 shows const order: Order. Both levels have what the type needs, so there are no errors, and line 6 prints Asha.

The type doesn't remove the extras. They're still on the object when the code runs. The type only decides what you may read through that name:

The extras are still there (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
type Contact = { name: string }; function showContact(contact: Contact) {  console.log(Object.keys(contact).join(", "));  console.log(contact.email);} const asha = { name: "Asha", email: "asha@example.com" };showContact(asha);

Line 1 says a Contact has only a name. Line 9 passes asha, who also has an email. Inside the function, line 4 lists the object's real property names with Object.keys and prints name, email: the email is there. Line 5 reads contact.email, and TypeScript marks it, on purpose: Property 'email' does not exist on type 'Contact'. That means "the type you gave this parameter doesn't list an email, so I can't promise there is one". Run it anyway, and line 5 prints asha@example.com.

There is one place where extra properties do get an error: an object you write right where the type is expected, like inside the call itself:

An object written inside the call (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
type User = { name: string }; function greet(user: User) {  return `Hi, ${user.name}`;} console.log(greet({ name: "Asha", email: "asha@example.com" }));

This is the overview's greet, but line 7 writes the object inside the call instead of passing a variable. TypeScript marks the email, on purpose: Object literal may only specify known properties, and 'email' does not exist in type 'User'. An object literal is an object written out with braces, like { name: "Asha" }. When it goes straight into a typed spot, nothing else can ever read its extra properties, so TypeScript guesses the extra is a mistake, often a typo. The code still prints Hi, Asha. The next lesson, Excess property checks, is all about this extra check.

Comparing with nominal typing

Many languages, like Java and C#, use nominal typing. "Nominal" means "by name": a value fits a type only if it was declared with that type's name, or with a type that says it extends it. TypeScript doesn't work that way, even for classes (Class basics covers JavaScript classes):

A plain object fits a class typePop out in the code editor (opens in a new tab)TypeScript
class Product {  name: string;  price: number;   constructor(name: string, price: number) {    this.name = name;    this.price = price;  }} function priceTag(product: Product) {  return `${product.name}: $${product.price}`;} console.log(priceTag(new Product("Tea", 3)));console.log(priceTag({ name: "Cake", price: 4 }));

Lines 1 to 9 make a Product class with a name and a price. Line 11 says priceTag takes a Product. Line 15 passes a real Product, made with new, and prints Tea: $3. Line 16 passes a plain object that was never made by the class. It has a name and a price, so it fits too, and prints Cake: $4. In Java, line 16 would be an error. (A class with private members is the exception; a later lesson, public, private, protected & #private, covers it. Classes in TypeScript covers the class syntax.)

The table below compares the two ways of checking. Read each row as a question, then each way's answer.

Nominal typing and structural typing
QuestionNominal (Java, C#)Structural (TypeScript)
What decides whether a value fits a type?The type's name: the value must be declared as that type, or as one that says it extends it.The shape: the value must have the properties the type lists, with types that fit.
Do two types with the same properties but different names match?No. Different names are different types.Yes. TypeScript sees one shape, so they're interchangeable.
Does a plain object fit a class type?No. Only objects made by that class, or a subclass, fit.Yes, if it has the class's properties and methods (classes with private members are the exception).
Who uses it?Languages like Java and C#.TypeScript, because JavaScript code passes plain objects around everywhere.

TypeScript works this way because JavaScript code passes plain objects around everywhere: replies from servers, options for functions, props in components. Checking shapes describes that code as it is.

Structural typing has one cost. When two kinds of value have the same shape, TypeScript can't tell them apart. IDs are the usual example:

A product ID passed as a user IDPop out in the code editor (opens in a new tab)TypeScript
type UserId = string;type ProductId = string; function loadUser(id: UserId) {  return `Loading user ${id}`;} const productId: ProductId = "p-7";console.log(loadUser(productId));

Lines 1 and 2 make two type aliases, UserId and ProductId. Line 4 says loadUser takes a UserId. Line 8 makes a product ID, and line 9 passes it to loadUser by mistake. There are no errors, and it prints Loading user p-7. A type alias doesn't make a new type. Both names mean string, and a string goes where a string goes.

Two name tags, UserId and ProductId, both point at the same card: string. A note says TypeScript can't tell them apart.UserIdProductIdstringcan't tell them apart
UserId and ProductId are both names for string. To TypeScript they're the same type, so a product ID goes where a user ID belongs without an error.

To keep them apart, give them different shapes. One way is to wrap each ID in an object with a kind property that must hold one exact text:

IDs with different shapes (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
type UserId = { kind: "user"; value: string };type ProductId = { kind: "product"; value: string }; function loadUser(id: UserId) {  return `Loading user ${id.value}`;} const productId: ProductId = { kind: "product", value: "p-7" };console.log(loadUser(productId));

In line 1, kind: "user" means the kind property must be exactly the text "user", not any string (the Literal types lesson covers this). Line 2 does the same with "product". Now the two shapes differ, and line 9 gets an error, on purpose: Types of property 'kind' are incompatible, and below it, Type '"product"' is not assignable to type '"user"'. The mix-up is caught before the code runs. A later lesson, Branded types, shows a way to keep IDs apart while they stay plain strings when the code runs.

One product card, two sources of data

A shop shows the same product card on the catalog page and in search results. The two lists come from different places, so their objects have different extra fields. With structural typing, one card function takes both:

One card function for two kinds of productPop out in the code editor (opens in a new tab)TypeScript
type CardProduct = { name: string; price: number; imageUrl: string }; function productCard(product: CardProduct) {  return `${product.name} ($${product.price}) ${product.imageUrl}`;} const fromCatalog = { id: 1, name: "Tea", price: 3, imageUrl: "/img/tea.png", stock: 9 };const fromSearch = { name: "Cake", price: 4, imageUrl: "/img/cake.png", score: 0.92 }; console.log(productCard(fromCatalog));console.log(productCard(fromSearch));

Line 1 lists the three things a card shows: a name, a price, and an image address. Line 7 is a catalog product, with an id and a stock as well. Line 8 is a search result, with a score that says how well it matched. Lines 10 and 11 pass both. Each has the three properties a card needs, so there are no errors, and the code prints Tea ($3) /img/tea.png and Cake ($4) /img/cake.png.

Now say the search service stops sending images:

A search result with no image (an error on purpose)Pop out in the code editor (opens in a new tab)TypeScript
type CardProduct = { name: string; price: number; imageUrl: string }; function productCard(product: CardProduct) {  return `${product.name} ($${product.price}) ${product.imageUrl}`;} const fromSearch = { name: "Cake", price: 4, score: 0.92 }; console.log(productCard(fromSearch));

Line 7's search result has no imageUrl. TypeScript marks line 9, on purpose: Property 'imageUrl' is missing. Plain JavaScript would draw a card with the image address undefined, and the code prints Cake ($4) undefined. Listing only what a function uses, as CardProduct does, keeps it open to every object that has those properties, and still catches the ones that don't.

Fits or doesn't fit?
  • const tea = { name: "Tea", price: 3 }; const label: { name: string } = tea;
  • const tea = { name: "Tea" }; const item: { name: string; price: number } = tea;
  • const tea = { name: "Tea", price: 3 }; const item: { name: number } = tea;
  • const reply = { id: 7, name: "Asha", email: "asha@example.com" }; const user: { name: string } = reply;
  • const user: { name: string } = { name: "Asha", age: 30 };
  • type Customer = { name: string }; const asha: Customer = { name: "Asha" }; const user: { name: string } = asha;
  • type UserId = string; type ProductId = string; const p: ProductId = "p-7"; const u: UserId = p;
  • const p: { kind: "product" } = { kind: "product" }; const u: { kind: "user" } = p;
Try it yourself
0 of 8 correct

Sort each line by what TypeScript says about it. Each line is checked on its own.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mix-ups

  • "An object has to be declared as a type to fit it." No. asha was never declared as a User, and it fits, because it has a name.
  • "Extra properties are always an error." No. Extras on a variable are fine. Only an object literal written right where the type is expected gets the extra check, which the next lesson covers.
  • "Two types with different names are different types." No. Customer and Subscriber have the same shape, so they're interchangeable.
  • "A type alias makes a new type." No. type UserId = string is just another name for string, so any string fits it, including a product ID.
  • "If A fits B, then B fits A." No. A Product fits where a CartItem goes, but a CartItem has no stock, so it doesn't fit where a Product goes.
  • "The type removes the extra properties." No. Types are gone when the code runs. The object keeps every property; the type only limits what you may read through that name.
  • "Classes are compared by name, as in Java." No. A plain object with the same properties fits a class type, unless the class has private members.

Practice exercises

Each exercise is TypeScript. When you fix code, press Edit & run on the starter code: the errors update as you type, and the console shows what it prints. Try a guess first, then use the hints.

Exercise 1 · Warm-upWill Asha fit Shopper?

Guess before you check: does TypeScript report an error for this code?

Starter codePop out in the code editor (opens in a new tab)TypeScript
type Shopper = { name: string };

function welcome(shopper: Shopper) {
  return `Welcome back, ${shopper.name}`;
}

const asha = { name: "Asha", cartItems: 3, vip: true };
console.log(welcome(asha));

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upFind the missing property

    TypeScript will mark line 8. Which property is missing from home? Then add it.

    Starter codePop out in the code editor (opens in a new tab)TypeScript
    type Address = { street: string; city: string; zip: string };
    
    function shippingLabel(address: Address) {
      return `${address.street}, ${address.city} ${address.zip}`;
    }
    
    const home = { street: "12 Park Road", city: "Pune" };
    console.log(shippingLabel(home));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeReshape a server reply

      The server calls its fields title and cost. Don't change CartItem or the reply. Make the call fit. What does your fixed code print?

      Starter codePop out in the code editor (opens in a new tab)TypeScript
      type CartItem = { name: string; price: number }; function lineText(item: CartItem) {  return `${item.name}: $${item.price}`;} const reply = { title: "Tea", cost: 3 };console.log(lineText(reply));

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeOne function for users and products

        userTag and productTag do the same job. Replace them with one function, nameTag, that takes either. What must its parameter's type list?

        Starter codePop out in the code editor (opens in a new tab)TypeScript
        type User = { name: string; email: string };type Product = { name: string; price: number }; function userTag(user: User) {  return `[${user.name}]`;} function productTag(product: Product) {  return `[${product.name}]`;} const asha: User = { name: "Asha", email: "asha@example.com" };const tea: Product = { name: "Tea", price: 3 };console.log(userTag(asha), productTag(tea));

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeCatch the ID mix-up

          TypeScript is happy with this code. What does it print? Then change the two ID types so TypeScript catches the mix-up on line 9.

          Starter codePop out in the code editor (opens in a new tab)TypeScript
          type UserId = string;type ProductId = string; function removeFromWishlist(id: ProductId) {  return `Removing product ${id}`;} const userId: UserId = "u-42";console.log(removeFromWishlist(userId));

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeWhich way does it fit?

            One of lines 6 and 7 gets an error. Which line? Then fix it without changing the types. The ... spread syntax helps (Spread and rest covers it).

            Starter codePop out in the code editor (opens in a new tab)TypeScript
            type Product = { name: string; price: number; stock: number };
            type CartItem = { name: string; price: number };
            
            const tea: Product = { name: "Tea", price: 3, stock: 9 };
            const cake: CartItem = { name: "Cake", price: 4 };
            const first: CartItem = tea;
            const second: Product = cake;

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each question, go through the type's list one property at a time: is it there, and does its type fit? The quiz shows its code without the checker, so the error list doesn't give the answers away.

              The structural typing quiz · 7 questionsScore: first tries count
              1. Question 1 of 7When TypeScript checks whether an object fits a type, what does it compare?

                Choose an answer to see the explanation.

              2. Question 2 of 7Does this welcome call get an error, and what prints?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                type Shopper = { name: string };
                
                function welcome(shopper: Shopper) {
                  return `Welcome, ${shopper.name}`;
                }
                
                const ravi = { name: "Ravi", points: 120 };
                console.log(welcome(ravi));

                Choose an answer to see the explanation.

              3. Question 3 of 7Which line of this cart code does TypeScript mark?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                type Product = { name: string; price: number; stock: number };
                type CartItem = { name: string; price: number };
                
                const cake: CartItem = { name: "Cake", price: 4 };
                const tea: Product = { name: "Tea", price: 3, stock: 9 };
                const a: CartItem = tea;
                const b: Product = cake;

                Choose an answer to see the explanation.

              4. Question 4 of 7What happens when a coupon ID goes where an order ID belongs?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                type OrderId = string;
                type CouponId = string;
                
                function cancelOrder(id: OrderId) {
                  return `Cancelled ${id}`;
                }
                
                const coupon: CouponId = "SAVE10";
                console.log(cancelOrder(coupon));

                Choose an answer to see the explanation.

              5. Question 5 of 7What does countFields print for Asha's contact?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                type Contact = { name: string };
                
                function countFields(contact: Contact) {
                  return Object.keys(contact).length;
                }
                
                const asha = { name: "Asha", email: "asha@example.com", phone: "555-0101" };
                console.log(countFields(asha));

                Choose an answer to see the explanation.

              6. Question 6 of 7In a nominally typed language like Java, when does a value fit a type?

                Choose an answer to see the explanation.

              7. Question 7 of 7Does this greet call, with the object written inside it, get an error?

                Read the code, then predictPop out in the code editor (opens in a new tab)TypeScript
                type Contact = { name: string };
                
                function greet(contact: Contact) {
                  return `Hi, ${contact.name}`;
                }
                
                console.log(greet({ name: "Asha", phone: "555-0101" }));

                Choose an answer to see the explanation.

              Key takeaways

              • TypeScript compares shapes, not names. A value fits a type when it has every property the type lists, each with a type that fits.
              • A value doesn't have to be declared as a type to fit it, and the order of properties doesn't matter.
              • Extra properties on a variable are fine. The type only limits what you may read; the extras are still there when the code runs.
              • The check has a direction: a bigger shape fits a smaller one, not the other way round.
              • Two types with the same shape are interchangeable, and a type alias like UserId = string is just another name.
              • To keep look-alike values apart, give them different shapes, like a kind property.

              Remember the one-liner.
              If it has the properties the type asks for, with the right types, it fits.

              Coming next: Excess property checks. It explains why an object literal with an extra property gets an error while the same object in a variable doesn't, and how that catches typos.

              CompleteFrontend Clear concepts. Working examples.