Why am I getting an error "Object literal may only specify known properties"?
Asked Answered
S

1

177

I just upgraded from TypeScript 1.5 to the latest and I'm seeing an error in my code:

interface Options {
   /* ... others ... */
   callbackOnLocationHash?: boolean;
}

function f(opts: Options) { /* ... */ }

//  Error: Object literal may only specify known properties,
//     and 'callbackOnLoactionHash'does not exist in type 'Options'.
f( { callbackOnLoactionHash: false });

Code looks fine to me. What's wrong?

(Alternative universe version: I recognize the typo, and I really did mean to write that. What should I do to remove the error?)

Swayback answered 4/8, 2015 at 17:55 Comment(2)
For visitors, this is because of TypeScript's concept of freshness: basarat.gitbooks.io/typescript/content/docs/types/…Mussorgsky
Since, for the time being, such a check is not run on intersection types, you can just avoid this by writing f(x: T & any) instead of f(x: T).Chromatics
S
268

As of TypeScript 1.6, properties in object literals that do not have a corresponding property in the type they're being assigned to are flagged as errors.

Usually this error means you have a bug (typically a typo) in your code, or in the definition file. The right fix in this case would be to fix the typo. In the question, the property callbackOnLoactionHash is incorrect and should have been callbackOnLocationHash (note the mis-spelling of "Location").

This change also required some updates in definition files, so you should get the latest version of the .d.ts for any libraries you're using.

Example:

interface TextOptions {
    alignment?: string;
    color?: string;
    padding?: number;
}
function drawText(opts: TextOptions) { ... }
drawText({ align: 'center' }); // Error, no property 'align' in 'TextOptions'

But I meant to do that

There are a few cases where you may have intended to have extra properties in your object. Depending on what you're doing, there are several appropriate fixes

Type-checking only some properties

Sometimes you want to make sure a few things are present and of the correct type, but intend to have extra properties for whatever reason. Type assertions (<T>v or v as T) do not check for extra properties, so you can use them in place of a type annotation:

interface Options {
    x?: string;
    y?: number;
}

// Error, no property 'z' in 'Options'
let q1: Options = { x: 'foo', y: 32, z: 100 };
// OK
let q2 = { x: 'foo', y: 32, z: 100 } as Options;
// Still an error (good):
let q3 = { x: 100, y: 32, z: 100 } as Options;

These properties and maybe more

Some APIs take an object and dynamically iterate over its keys, but have 'special' keys that need to be of a certain type. Adding a string indexer to the type will disable extra property checking

Before

interface Model {
  name: string;
}
function createModel(x: Model) { ... }

// Error
createModel({name: 'hello', length: 100});

After

interface Model {
  name: string;
  [others: string]: any;
}
function createModel(x: Model) { ... }

// OK
createModel({name: 'hello', length: 100});

This is a dog or a cat or a horse, not sure yet

interface Animal { move; }
interface Dog extends Animal { woof; }
interface Cat extends Animal { meow; }
interface Horse extends Animal { neigh; }

let x: Animal;
if(...) {
  x = { move: 'doggy paddle', woof: 'bark' };
} else if(...) {
  x = { move: 'catwalk', meow: 'mrar' };
} else {
  x = { move: 'gallop', neigh: 'wilbur' };
}

Two good solutions come to mind here

Specify a closed set for x

// Removes all errors
let x: Dog|Cat|Horse;

or Type assert each thing

// For each initialization
  x = { move: 'doggy paddle', woof: 'bark' } as Dog;

This type is sometimes open and sometimes not

A clean solution to the "data model" problem using intersection types:

interface DataModelOptions {
  name?: string;
  id?: number;
}
interface UserProperties {
  [key: string]: any;
}
function createDataModel(model: DataModelOptions & UserProperties) {
 /* ... */
}
// findDataModel can only look up by name or id
function findDataModel(model: DataModelOptions) {
 /* ... */
}
// OK
createDataModel({name: 'my model', favoriteAnimal: 'cat' });
// Error, 'ID' is not correct (should be 'id')
findDataModel({ ID: 32 });

See also https://github.com/Microsoft/TypeScript/issues/3755

Swayback answered 4/8, 2015 at 17:55 Comment(7)
i'm thinking how did he making a spelling error with typescript, isnt the main advantage of typescript autocompletionRachealrachel
I would posit that the main advantage of typescript is type safety and reducing the amount of bugs and potentially dangerous code we create - autocomplete is just an added bonus.Claud
A "typo" can also manifest if, for example, you change an API somewhere and intentionally break downstream code.Swayback
How do I check that this answer is still valid in 2021?Rufescent
@Rainning copy and paste will always be validStage
Typo it was 🤦‍♂️Ngo
This is a great answer, but wouldn't interface UserProperties { userKey1: string; userKey2: number; } be better than interface UserProperties { [key: string]: any; }, when possible? The former will still protect you from typos and typecheck the values. The latter allows any property name and value type. This was written long ago, so that may not have been an option then.Jeanejeanelle

© 2022 - 2024 — McMap. All rights reserved.