Execute Program

Everyday TypeScript: Object Literal May Only Specify Known Properties

Welcome to the Object Literal May Only Specify Known Properties lesson!

This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!

  • We've seen that TypeScript's structural typing allows extra properties on objects. Remember that "structural typing" means that {name: string, age: number} is compatible with the type {name: string}. This works because the name property is present and its type matches.

  • The function below expects a {name: string}, but we pass a {name: string, age: number}. TypeScript allows the extra age property.

  • >
    function userName(user: {name: string}) {
    return user.name;
    }

    const user = {name: 'Amir', age: 36};
    userName(user);
    Result:
    'Amir'Pass Icon
  • This idea is central to TypeScript's type system. It's also unusual: most static languages would reject that code. However, there are situations where TypeScript explicitly disallows structural typing. For example:

    1. When we pass literal objects directly to functions
    2. When we assign literal objects directly to variables with explicit types
  • If we pass the literal object {name: 'Amir', age: 36} directly to a function expecting a {name: string}, there's no possible use for age. It's not part of the {name: string} type, so no code can ever access it. It must be a mistake, and TypeScript highlights that mistake with a type error.

  • >
    function userName(user: {name: string}) {
    return user.name;
    }

    userName({name: 'Amir', age: 36});
    Result:
    type error: Object literal may only specify known properties, and 'age' does not exist in type '{ name: string; }'.Pass Icon
  • What if we put {name: 'Amir', age: 36} in a variable, then pass the variable to the function? That's a very different situation! For all TypeScript knows, we might pass that variable to dozens of other functions, some of which access the age property. TypeScript allows it.

  • >
    function userName(user: {name: string}) {
    return user.name;
    }

    const user: {name: string, age: number} = {name: 'Amir', age: 36};
    userName(user);
    Result:
    'Amir'Pass Icon
  • The same logic applies to variables with explicit types. We can't say "this object is a {name: string}", but then immediately give it a value of {name: 'Amir', age: 36}. The age property would never be accessible for the same reason explained above, so it must be a mistake.

  • Here are two similar code examples to isolate the difference. Only one of them causes a type error.

  • >
    const user: {name: string} = {name: 'Amir', age: 36};
    user;
    Result:
    type error: Object literal may only specify known properties, and 'age' does not exist in type '{ name: string; }'.Pass Icon
  • >
    const user: {name: string, age: number} = {name: 'Amir', age: 36};
    const nameOnly: {name: string} = user;
    const ageOnly: {age: number} = user;
    [nameOnly.name, ageOnly.age];
    Result:
    ['Amir', 36]Pass Icon
  • This "object literal may only specify known properties" type error seems unimportant at first, but it can prevent some subtle mistakes. For example, suppose that our function takes an optional age property. We can provide it or not.

  • >
    function userNameAndAge(user: {name: string, age?: number}) {
    if (user.age === undefined) {
    return user.name;
    } else {
    return `${user.name}, ${user.age}`;
    }
    }

    [
    userNameAndAge({name: 'Amir', age: 36}),
    userNameAndAge({name: 'Betty'}),
    ];
    Result:
    ['Amir, 36', 'Betty']Pass Icon
  • Now suppose that we make a typo in the age property: we type aeg instead. That changes the type in two ways. First, the age property is missing. TypeScript allows that because the age property is optional. Second, there's an unexpected aeg property.

  • In the next example, we make exactly that aeg mistake. TypeScript allows it!

  • >
    function userNameAndAge(user: {name: string, age?: number}) {
    if (user.age === undefined) {
    return user.name;
    } else {
    return `${user.name}, ${user.age}`;
    }
    }

    const user = {name: 'Amir', aeg: 36};
    userNameAndAge(user);
    Result:
    'Amir'Pass Icon
  • Unfortunately, TypeScript can't save us from that bug. Optional properties combined with structural typing make that code perfectly legal.

  • However, TypeScript can save us from similar mistakes in other situations. The example below is the same as the one above, but with one critical difference.

  • Above, we created a user variable with the inferred type {name: string, aeg: number}, then passed it to the function. Below, we pass the same {name: string, aeg: number} object directly to the function, with no variable involved. This time, TypeScript gives us the same type error that we saw earlier in this lesson.

  • >
    function userNameAndAge(user: {name: string, age?: number}) {
    if (user.age === undefined) {
    return user.name;
    } else {
    return `${user.name}, ${user.age}`;
    }
    }

    // We make a typo in this property name, but TypeScript prevents the bug!
    userNameAndAge({name: 'Amir', aeg: 36});
    Result:
    type error: Object literal may only specify known properties, and 'aeg' does not exist in type '{ name: string; age?: number | undefined; }'.Pass Icon
  • TypeScript has helped us out by showing us the aeg typo! This only happens because of the special rule for object literals. Otherwise, structural typing would allow that code to type check.

  • This "object literal may only specify known properties" error is very common in TypeScript. It's usually easy to fix, and it occasionally saves us from property name typos like aeg!

  • Here's a code problem:

    The code below calls an albumSales function, but our object has an extra property that isn't in the Album type. That's a type error: TypeScript doesn't allow extra properties on literal objects. Fix the type error by removing the extra property.

    type Album = {name: string, copiesSold: number};

    function albumSales(album: Album): number {
    return album.copiesSold;
    }
    const sales = albumSales({
    name: 'Kind of Blue',
    copiesSold: 4490000,
    });
    sales;
    Goal:
    4490000
    Yours:
    4490000Pass Icon
  • To summarize this lesson:

    • In general, TypeScript allows objects to have extra properties. For example, we can pass a variable with the type {name: string, age: number} to a function expecting a {name: string}.
    • But TypeScript doesn't allow extra properties when passing literal objects to functions, or assigning them directly to a variable with an explicit type. If we have function userName(user: {name: string}), then userName({name: 'Amir', age: 36}) is a type error. TypeScript knows that no code will ever access that age property, so there's no reason for it to exist.
    • That can show us subtle bugs like misspelled property names.