Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
declare const EmailTag: unique symbol;
type Email = string & typeof EmailTag;
function isEmail(email: string): email is Email {
return /@/.test(email);
function createEmail(email: string): Email;
function createEmail(username: string, domain?: string): Email {
if (domain) {
return <Email>`${username}@${domain}`;
if (isEmail(username)) {
return username;
} else {
throw new Error(`Not a valid email address: ${username}`);
namespace Backend {
export type Recipients = string[];
const backend = {
async performRequest(url): Promise<any> {
return [""];
async getRecipients(): Promise<Email[]> {
let recipients: Backend.Recipients = await this.performRequest("recipients.json");
return => createEmail(address)));
// Without Tag types `to`` and `from`` are just strings.
// So if you want to refactor this function so subject comes last
// and becomes optional, you get no help from the type checker.
function sendEmail(subject: string, to: Email, from: Email): void {
const OUR_ADDRESS = <Email>"";
async function sendEmails() {
let recipients = await backend.getRecipients();
for (let recipient of recipients) {
sendEmail("Isn't TypeScript Great?", OUR_ADDRESS, recipient);
// If you get the order wrong you get a compiler error!
// Argument of type '"Isn't TypeScript Great?"' is not assignable to parameter of type 'Email'.
// Type '"Isn't TypeScript Great?"' is not assignable to type 'unique symbol'.
sendEmail(OUR_ADDRESS, recipient, "Isn't TypeScript Great?");
interface EmailLookup {
// can't be a tagged string, is that ok?
// [email: Email]: Email;
[email: string]: Email;
let emails: EmailLookup = {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.