You have a few options:
You can use an index signature type that allows any string or any number, for instance:
let myObj: {[key: string]: number} = {}; myObj[prop] = value;...allows any string property name, with the values expected to be numbers. (Or you can use
{[key: number]: number}to make the keys numbers.stringandnumberare your only two index signature options.)You can use the
Recordutility type, for exampleRecord<string, number>which does much the same thing as the above:let myObj: Record<string, number> = {}; myObj[prop] = value;You can use a
Mapinstead of an object:let myObj: Map<string, number> = new Map(); myObj.set(prop, value);
In all three of those, number as the value type is just an example, it can be any type you want (including a union type), or unknown if you won't know the type, or any if you want to allow any type.
It may be obvious, but I'll say it anyway: This does mean TypeScript can't help you with using the wrong property name on the object. :-) But that's inevitable if the property name is a runtime thing.
How to access a possibly empty array by index?
TypeScript empty object for a typed variable - Stack Overflow
Intersection of empty object type with empty object type is missing index signature
How to assign the appropriate types to this empty object?
Videos
this is my situation
let matches : string[] | null = topic.match(this._DEVICE_TOPIC_REGEXP);
console.log("handleNewMeasure, topic", topic, "matches", matches);
if (typeof matches === undefined ) {
return;
}
let device_id = matches[1];
I cannot go on because last line warns me that it could be empty
Caveats
Here are two worthy caveats from the comments.
Either you want user to be of type
User | {}orPartial<User>, or you need to redefine theUsertype to allow an empty object. Right now, the compiler is correctly telling you that user is not a User. –jcalz
I don't think this should be considered a proper answer because it creates an inconsistent instance of the type, undermining the whole purpose of TypeScript. In this example, the property
Usernameis left undefined, while the type annotation is saying it can't be undefined. –Ian Liu Rodrigues
Answer
One of the design goals of TypeScript is to "strike a balance between correctness and productivity." If it will be productive for you to do this, use Type Assertions to create empty objects for typed variables.
type User = {
Username: string;
Email: string;
}
const user01 = {} as User;
const user02 = <User>{};
user01.Email = "[email protected]";
Here is a working example for you.

An empty object can be written as Record<string,never>, so effectively your type for user is either an empty object or a User
const user : User | Record<string, never> = {};
This is what I have so far
interface Book {
"book-title": string;
"id": string;
"description": string;
"tagId": string;
"tagName": string;
}
const hydrateNotes = (input: Book[]) => {
const lookup = {};
for (let note of input) {
if (!lookup[note.id]) {
lookup[note.id] = note;
lookup[note.id].tags = [];
}
if (note.tagId && note.tagName) {
lookup[note.id].tags.push({
id: note.tagId,
name: note.tagName,
});
}
}
return lookup;
};I'm getting errors on lookup[note.id] saying No index signature with a parameter of type 'string' was found on type '{}
The input is an array of Book interface, and the end result for lookup would be like this jsbin
TypeScript > v4.1 has the option noUncheckedIndexedAccess which should return T | undefined for all unknown index access.
You could implement your own last and be more accurate in its typing:
function last<T>(array: T[]): T | undefined // Explicit type
{
return array[array.length - 1];
}
The best solution I could come up with was to use last from lodash and adding it as a separate package. I also added type definitions separately by installing @types/lodash.last.
My example case above would end up looking like this:
import last from 'lodash.last'
const game: ISet[] = [];
const currentSet = last(game); // 'currentSet' now has a type of 'ISet | undefined'
console.log(currentSet.score); // Object is possibly 'undefined'. ts(2532)
The definition for Record is:
/**
* Construct a type with a set of properties K of type T
*/
type Record<K extends keyof any, T> = {
[P in K]: T;
};
When creating a type like type MyType = Record<string, string>;, inlining Record leads to the following type:
type MyType = {
[P in string]: string;
};
This is saying to create an object type with string property names within the set string. Since string is unbounded there's unlimited possibilities of strings (unlike a union of string literal types like "prop1" | "prop2")... so it's describing an object that can have any number of properties with any name, with the only restriction being that the properties must have a type of string.
So yes, from a type checking perspective it's basically equivalent to the example with the index signature without a mapped type ({ [index: string]: string; }.
Use a plain index signature
Using Record in this fashion is a little strange though and many people might not understand what's going on. A more common way to express intent when there can be any number of properties, is to not use a mapped type:
type ObjectWithStringProperties = {
[index: string]: string;
};
This has the added benefit of helping explain what the key is supposed to be. For example:
type PersonsByName = {
[name: string]: Person;
};
const collection: PersonsByName = {};
Note that in this way the types are different because a developer using an object with this type will have this extra described key name information to look at in their editor.
Using Record
Note that Record is usually used like the following:
type ThreeStringProps = Record<"prop1" | "prop2" | "prop3", string>;
// goes to...
type ThreeStringProps = { [P in "prop1" | "prop2" | "prop3"]: string; };
// goes to...
type ThreeStringProps = {
prop1: string;
prop2: string;
prop3: string;
};
Whether it is a good idea to use Record instead of a plain index signature may be a matter of debate (as David Shereet points out in his answer). Also the fact that you can do a lot more thing with Record then you can with a simple index signature is also something that should be mentioned.
The main part of this question (in my reading) is whether the two types are the same. They are obviously declared in different ways but are they the same type. While they are obviously compatible (that is you can assign one to the other and vice-versa) the question is are there corner cases where this is not possible.
While it's hard to find an exhaustive list of what you can do with a type, Matt McCutchen in this answer provides an interesting type that detects weather the readonly modifier is present (something that simple compatibility does not detect the difference between). I would surmise that if Record and an index signature are the considered the same in the way Matt uses them there (as part of the signature of a generic function) they are pretty much the same type declared in a different way:
type IfEquals<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? "Y" : "N";
let same : IfEquals<{x: string}, {x: string}>= "Y"
let notsame : IfEquals<{ y: string }, { x: string }>= "N"
let notsamero: IfEquals<{ readonly x: string }, { x: string }> = "N"
let samerecord: IfEquals<{ [x: string]:string }, Record<string, string>> = "Y"
As we can see in the last example the type of samerecord is Y meaning that the compiler treated the two types as being the same thing. Thus I would surmise { [x: string]:string } and Record<string, string> are exactly the same thing.
I know this is a really basic question, and I've tried to look at similar questions and didn't understand it so I apologize.
I'm translating my JavaScript code to TypeScript, and have a lot of code that looks similar to this:
if (!user_dict.options) {
user_dict.options = {}
}This produces the error "property options does not exist on type {}" for both lines. How can I establish an empty object like this, if I don't know what type of variables will later be stored? I have hundreds of empty variable declarations like this, and I can't know at this point in the code the "type" of variable being stored later, so I don't understand how I'm supposed to handle this using TypeScript?
I have hundreds of empty variable declarations like this, and I can't know at this point in the code the "type" of variable being stored later,
You don't know what sort of data you're planning on putting in the variable?
"property options does not exist on type {}"
Do it basically tells you that user_dict type doesn’t contain ‘options’ property. You need to add it to user_dict type or interface. Or if you don’t have type for this object you’d better write it.
items: object[],
While technically it is a JavaScript object, the type can be better. For Typescript to correctly help you identify mistakes when accessing objects properties, you need to tell it the exact shape of the object. If you type it as object, typescript cannot help you with that. Instead you could tell it the exact properties and datatypes the object has:
let assistance: { safe: string } = { safe: 1 /* typescript can now tell this is wrong */ };
assistance.unknown; // typescript can tell this wont really work too
Now in the case that the object can contain any sort of key / value pair, you can at least tell typescript what type the values (and the keys) have, by using an object index type:
items: {
[key: string]: number | string,
}[]
That would be the accurate type in the case given.
First of all, defining the type of items as an array of objects is a bad idea, as it defeats the purpose of Typescript. Instead define what type of objects the array will contain. I would do it this way:
type Item = {
id: number,
name: string,
email: string,
}
export type TableGridViewProps = {
items: Item[],
tableColumns: TableColumn[]
};
After that if you're absolutely sure that the key would exist in item, you can do the following to tell Typescript that you are accessing a valid index.
<td
key={column.key}
className="lorem ipsum"
>
{item[column.key as keyof typeof Item]}
</td>