Type Dataset Values in TypeScript
Learn how TypeScript types the dataset property on HTML elements. Understand DOMStringMap, camelCase conversion, and how to read and write data attributes safely.
HTML elements can carry custom data attributes that start with data-. TypeScript types these through the dataset property, which is typed as DOMStringMap. Every value you read or write through dataset is a string, and the attribute names follow a camelCase conversion rule that TypeScript understands but does not enforce at the type level.
Dataset values let you attach arbitrary metadata to DOM elements without using non-standard attributes. The browser stores them in the DOM, and JavaScript reads them through a clean object-like interface.
How dataset typing works
The dataset property on any HTMLElement is typed as DOMStringMap. This is a map-like interface where every key is a string and every value is a string. When you read dataset.userId, TypeScript knows the result is a string.
const card = document.querySelector<HTMLDivElement>('#card');
if (card === null) throw new Error('Card not found');
const userId = card.dataset.userId;
// ^? const userId: string | undefinedThe HTML that makes this work needs a matching data-user-id attribute on the same element, written directly in markup like this:
<div id="card" data-user-id="abc-123">Card content</div>The attribute name data-user-id becomes dataset.userId in JavaScript. TypeScript types the result as string or undefined, since it cannot know whether the HTML actually has a matching data attribute. The browser handles the name conversion automatically at runtime.
Data attribute name conversion
The camelCase conversion follows a simple rule: remove the data- prefix, then for each dash followed by a lowercase letter, remove the dash and uppercase the letter.
| HTML attribute | dataset property |
|---|---|
| data-id | dataset.id |
| data-user-id | dataset.userId |
| data-date-of-birth | dataset.dateOfBirth |
| data-max-retry-count | dataset.maxRetryCount |
TypeScript does not validate that your dataset property names match actual data attributes in the HTML. The DOMStringMap type accepts any string key. The browser silently returns undefined for properties that have no matching data attribute.
Reading and writing dataset values
You read a dataset value by accessing the camelCase property, typed as string or undefined. You write a value by assigning a string to the same property:
const el = document.querySelector<HTMLDivElement>('#user');
if (el === null) throw new Error('Element not found');
// Reading, always a string or undefined
const status = el.dataset.status;
console.log(status); // e.g., "active"
// Writing, assigns a string
el.dataset.status = 'inactive';To remove a data attribute entirely, use the delete operator on the dataset property. This removes both the JavaScript property and the HTML attribute from the element:
delete el.dataset.status;
// The data-status attribute is now gone from the HTMLAfter deletion, accessing el.dataset.status returns undefined, which is consistent with how DOMStringMap behaves for missing keys.
Working with non-string data
DOMStringMap only stores strings, and TypeScript enforces that at compile time. Assigning a number or boolean directly is a compiler error, unlike a plain HTML attribute where the browser would silently convert the value:
el.dataset.count = 5;TypeScript rejects this assignment before your code ever runs. The dataset property signature only accepts string values, so passing a number fails the same way any other type mismatch would fail elsewhere in your code:
Type 'number' is not assignable to type 'string'.Fix the error by converting the value to a string yourself before assigning it. String() works for numbers, booleans, and most other primitive values you want to store as a data attribute:
el.dataset.count = String(5); // stored as "5"
el.dataset.active = String(true); // stored as "true"When you read these values back, you need to parse them into the expected type yourself. TypeScript cannot know the intended type of a dataset value, so it always gives you string or undefined:
const count = Number(el.dataset.count);
// ^? const count: number
const active = el.dataset.active === 'true';
// ^? const active: booleanAlways validate parsed values. If someone sets the data-count attribute to "abc" in the HTML, Number will return NaN. A quick check with Number.isNaN prevents downstream bugs.
Checking if a dataset property exists
Use the in operator or check against undefined to test whether a data attribute exists on an element:
if ('userId' in el.dataset) {
console.log('User ID is set');
}
if (el.dataset.userId !== undefined) {
console.log('User ID is set');
}Both approaches work at runtime. The in operator checks the DOMStringMap object, and the undefined check tests the property value. They produce the same result for dataset properties in practice.
Dataset vs getAttribute
You can also read data attributes with getAttribute and setAttribute. These methods use the full HTML attribute name and are typed through the Element interface:
const userId = el.getAttribute('data-user-id');
el.setAttribute('data-user-id', 'new-value');getAttribute returns string or null instead of string or undefined, and it takes the full attribute name rather than the camelCase form. Both approaches are valid TypeScript, so pick the one that fits how you read the result:
| Approach | Return type | Attribute name |
|---|---|---|
| element.dataset.userId | string or undefined | camelCase, data- prefix removed |
| element.getAttribute('data-user-id') | string or null | Full HTML attribute name |
Use dataset when you want the camelCase shorthand and the object-like interface. Use getAttribute when you need the exact HTML attribute name or prefer a null return for missing attributes over undefined.
See Type DOM Elements in TypeScript for how element types work. See Type Form Elements in TypeScript for form field typing patterns.
Rune AI
Key Insights
- element.dataset is typed as DOMStringMap, where reading a property gives string or undefined.
- Data attributes use camelCase conversion: data-user-id becomes dataset.userId.
- Assigning a number or boolean to a dataset property is a compile-time error; convert it with String() first.
- Use delete to remove a dataset property and the corresponding data attribute.
- Parse numeric and boolean values manually after reading from dataset.
Frequently Asked Questions
What type does element.dataset have in TypeScript?
How does data-attribute name conversion work with dataset?
Can I store non-string values in dataset?
Conclusion
TypeScript types element.dataset as DOMStringMap, where every property reads as string or undefined and every write must be a string. HTML data attributes like data-user-id become dataset.userId through camelCase conversion. Always parse string values back to the expected type when reading, and remember that dataset access is always a browser API, not a compile-time checked type system.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.