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.

6 min read

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.

typescripttypescript
const card = document.querySelector<HTMLDivElement>('#card');
if (card === null) throw new Error('Card not found');
 
const userId = card.dataset.userId;
//    ^? const userId: string | undefined

The HTML that makes this work needs a matching data-user-id attribute on the same element, written directly in markup like this:

htmlhtml
<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 attributedataset property
data-iddataset.id
data-user-iddataset.userId
data-date-of-birthdataset.dateOfBirth
data-max-retry-countdataset.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:

typescripttypescript
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:

typescripttypescript
delete el.dataset.status;
// The data-status attribute is now gone from the HTML

After 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:

typescripttypescript
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:

texttext
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:

typescripttypescript
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:

typescripttypescript
const count = Number(el.dataset.count);
//    ^? const count: number
 
const active = el.dataset.active === 'true';
//    ^? const active: boolean

Always 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:

typescripttypescript
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:

typescripttypescript
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:

ApproachReturn typeAttribute name
element.dataset.userIdstring or undefinedcamelCase, data- prefix removed
element.getAttribute('data-user-id')string or nullFull 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

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.
RunePowered by Rune AI

Frequently Asked Questions

What type does element.dataset have in TypeScript?

element.dataset is typed as DOMStringMap, an object-like interface where every property reads as string or undefined. You read and write data attributes using camelCase property names, and every value you write must be a string.

How does data-attribute name conversion work with dataset?

The HTML attribute data-user-id becomes dataset.userId in JavaScript. The data- prefix is removed, and any dash followed by a lowercase letter becomes an uppercase letter without the dash. Reading a property that has no matching attribute gives undefined.

Can I store non-string values in dataset?

Not directly. DOMStringMap only accepts strings, so assigning a number or boolean is a compile-time error in TypeScript. Convert the value with String() first, then parse it back to the desired type when you read it.

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.