More than happy to be informed of a better solution here! But this came up in a real-work situation and I "stumbled" on the solution by more or less guessing.

In plain JavaScript, you have an object which you know you set certain keys on. But because this object is (ab)used for a templating engine, we also put keys/values on it that are not known in advance. In our use case, these keys and booleans came from parsing a .yml file which. It looks something like this:


// Code simplified for the sake of the example

const context = {
  currentVersion: "3.12", 
  currentLanguage: "en",
  activeDate: someDateObject,
  // ... other things that are values of type number, bool, Date, and string
  // ...
}
if (someCondition()) {
  context.hasSomething = true
}

for (const [featureFlag, truth] of Object.entries(parseYamlFile('features.yml')) {
  context[featureFlag] = truth
}

const rendered = render(template: { context })

I don't like this design where you "combine" an object with known keys with a spread of unknown keys coming from an external source. But here we are and we have to convert this to TypeScript, the clock's ticking!

In comes TypeScript

Intuitively, from skimming the simplified pseudo-code above you might try this:


type Context = {
  currentVersion: string
  currentLanguage: string
  activeDate: Date
  [featureFlag: string]: boolean
}

TS error

TypeScript Playground demo here

Except, it won't work:

Property 'currentVersion' of type 'string' is not assignable to 'string' index type 'boolean'.
Property 'currentLanguage' of type 'string' is not assignable to 'string' index type 'boolean'.
Property 'activeDate' of type 'Date' is not assignable to 'string' index type 'boolean'.

Make sense, right? We're saying the type should have this, that, and that, but also saying that it can be anything. So it's a conflict.

How I solved it

I'll be honest, I'm not sure this is very intuitive, either. But it works:


type FeatureFlags = {
  [featureFlag: string]: boolean
}
type Context = FeatureFlags & {
  currentVersion: string
  currentLanguage: string
  activeDate: Date
}

TypeScript Playground demo

It does imply that the inheritance, using the & is more than just semantic sugar. It means something.

Comments

Your email will never ever be published.

Previous:
Rate my golf swing (June 2024) July 1, 2024 Golf
Next:
Converting Celsius to Fahrenheit with Python July 12, 2024 Python
Related by category:
How to SSG a Vite SPA April 26, 2025 JavaScript
Switching from Next.js to Vite + wouter July 28, 2023 JavaScript
An ideal pattern to combine React Router with TanStack Query November 18, 2024 JavaScript
get in JavaScript is the same as property in Python February 13, 2025 JavaScript
Related by keyword:
Run TypeScript in Node without extensions December 10, 2024 Node, JavaScript
Node watch mode and TypeScript July 21, 2024 Node, JavaScript
How to extend a function in TypeScript without repeating the signature types October 16, 2024 JavaScript
Simple object lookup in TypeScript June 14, 2024 JavaScript