> ## Content Index
> Fetch the complete content index at: https://madewithlove.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Creating a Scientific Pitch Notation Type using template literal types
- URL: https://madewithlove.com/blog/creating-a-scientific-pitch-notation-type-using-template-literal-types/
- Published: 2021-02-24T11:05:13.000Z
- Updated: 2026-05-29T11:57:31.000Z
- Author: Geoffrey Dhuyvetters
- Tags: Engineering, TypeScript, JavaScript, Frontend

Ok. So. The honeymoon phase is over. I can say TypeScript is steadily becoming a part of my daily stack. While working on converting [music-fns](https://github.com/madewithlove/music-fns) from Flow to TypeScript I bumped into a feature I didn’t know existed. But first, a little bit of context.

music-fns is a utility library that provides a set of functions to work with music notation. You can generate chords, melodies, calculate intervals, frequencies, etc. I like to pitch it (pun unintended) as “lodash for music.”

Internally, I have a noteToObject function that parses a note (written in [scientific pitch notation](https://en.wikipedia.org/wiki/Scientific%5Fpitch%5Fnotation)) to an object. That object contains the note, accidental, and octave information that I use for various operations.

#### Some examples of valid scientific pitch notation:

- A
- Ab
- A#4
- B3
- F♯2
- G♭

In my codebase, I would type this as a string.

`**type** ScientificNote = string;`

But, by using template literal types, we can narrow it down — a lot. This feature [introduced in TypeScript 4.1](https://devblogs.microsoft.com/typescript/announcing-typescript-4-1-beta/#template-literal-types) enables us to use [template literals](https://developer.mozilla.org/nl/docs/Web/JavaScript/Reference/Template%5Fliterals) when constructing types.

A valid scientific note contains:

- a **note** (A – G): C – D – E – F – G – A – B, where C is a “Do”.

and optionally 

- an **accidental**: symbol or letter equivalent (for convenience)
- an **octave**: a number

Let’s construct our new type.

```typescript
type Flat = 'b'| '♭'
type Sharp = '#'| '♯'
type Accidental = Flat | Sharp
type Octave = number
type Note = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G'
type ScientificNote = `${Note}${Accidental | ''}${Octave | ''}`
const note1: ScientificNote = 'Ab3' // VALID
const note2: ScientificNote = 'G' // VALID
const note3: ScientificNote = '333' // NOT VALID
const note4: ScientificNote = 4 // NOT VALID
const note5: ScientificNote = false // NOT VALID
const note6: ScientificNote = 'H#' // NOT VALID
```

The type above covers a lot but fails to catch ‘impossible notes’.

Have a look at the black keys of a piano. The white keys are natural notes, the black keys are accidentals. There, you **don’t have a sharp** between **B and C** & between **E and F** (although this might trigger [some](https://www.youtube.com/watch?v=FDzw1jByVDA) [discussions](https://www.youtube.com/watch?v=auHqn3kxDiU). I might revisit this decision later)**.** If you know the rules (notes + locate the ‘middle C’) it should be easy enough to translate notation to keys on a piano.

![keyboard pitch scientific pitch notation](https://madewithlove.com/blog/content/images/2026/05/keyboard-1024x342.jpg)

Since every sharp (‘#’| ‘♯’) can be translated to a flat (‘b’| ‘♭’). We have 4 ‘impossible notes’. By using Exclude and template literal types we can cover this with TypeScript.

```typescript
type Flat = 'b'| '♭'
type Sharp = '#'| '♯'
type Accidental = Flat | Sharp
type Octave = number
type Note = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G'
type ImpossibleNotes = `${'E' | 'B'}${Sharp}` | `${'F' | 'C'}${Flat}`;

export type ScientificNoteWithAccidental = Exclude<
  `${Note}${Accidental}`,
  ImpossibleNotes
>;
export type ScientificNote = `${ScientificNoteWithAccidental | Note}${
  | Octave
  | ''}`;
const note1: ScientificNote = 'Ab3' // VALID
const note2: ScientificNote = 'G' // VALID
const note3: ScientificNote = '333' // NOT VALID
const note4: ScientificNote = 4 // NOT VALID
const note5: ScientificNote = false // NOT VALID
const note6: ScientificNote = 'H#' // NOT VALID
const note7: ScientificNote = 'B#' // NOT VALID
const note8: ScientificNote = 'B♯3' // NOT VALID
const note9: ScientificNote = 'F♭3' // NOT VALID
```

Have fun experimenting with this new and powerful feature!

PS: There is also an issue open for [RegExp type definitions](https://github.com/microsoft/TypeScript/issues/41160) that is worth checking out (just imagine the possibilities).