Skip to content

Instantly share code, notes, and snippets.

@vasilyev-maksim
Last active January 12, 2021 22:49
Show Gist options
  • Save vasilyev-maksim/a791e273b21917be9ddc33b930954829 to your computer and use it in GitHub Desktop.
Save vasilyev-maksim/a791e273b21917be9ddc33b930954829 to your computer and use it in GitHub Desktop.
Code chunks for "Discriminated unions in Typescript" articles
// 👎 (classic)
interface IService {
a(): Promise;
b(): Promise;
c(): Promise;
}
// 🤟 (flow-based)
interface IService {
a(): Promise<{
b(): Promise<{
c(): Promise;
}>;
}>;
}
// 🤟
type IAsyncData<T> =
| { status: 'initial' }
| { status: 'loading'; initial: true }
| { status: 'loading'; initial: false; data: T }
| { status: 'success'; data: T }
| { status: 'error'; error: any };
// 👎
interface IAsyncData<T> {
status: 'initial' | 'loading' | 'success' | 'error';
data?: T;
error?: any;
}
interface IAuthor {
name?: string;
avatarUrl?: string;
}
interface IAuthor {
kind: 'anonym' | 'user'; // here we use regular union type
name?: string;
avatarUrl?: string;
}
type IAuthor = {
kind: 'user';
name: string;
avatarUrl: string;
} | { kind: 'anonym'; }
interface IComment {
id: string;
postId: string;
postedAt: Date;
author: IAuthor;
}
// 👎
type IDocument = {
id: string;
createdAt: Date;
status: 'draft' | 'signed';
signedAt?: Date;
};
// 🤟
type IDocument = {
id: string;
createdAt: Date;
} & (
| { status: 'signed'; signedAt: Date; }
| { status: 'draft'; }
);
type IValidationResult<T> =
| { valid: 'true'; }
| { valid: 'false'; errors: T; }
// for example
type ISignUpFormValidationResult = IValidationResult<{
email: ('empty' | 'invalid_format' | 'already_taken' | ...)[];
password: ('empty' | 'too_short' | 'too_simple' | ...)[];
}>
const comment = await getCommentById(42);
// bad idea, but typescript stays silent
console.log(comment.author.name); // possibly undefined
// good idea
if (comment.author.kind === 'user') {
if (comment.author.name != null) {
console.log(comment.author.name);
} else {
// server error
console.log('unknown');
logDataIntegrityError(comment.author);
}
} else {
console.log('anonym');
}
const comment = await getCommentById(42);
// typescript doesn't know whether the author is a user or an anonym here,
// so it throws an error
console.log(comment.author.name);
~~~~
[Property 'name' does not exist on type '{ kind: "anonym"; }'.ts(2339)]
if (comment.author.kind === 'user') {
// typescript understands that the author is a user in this scope,
// so here is no error
console.log(comment.author.name);
}
// 👎
type IAuthor = {
kind: 'user';
id: string;
name: string;
avatarUrl: string;
} | {
kind: 'anonym';
id: string; // temporary id stored in cookies, for example
}
// 🤟
type IAuthor = { id: string; } // `id` is not duplicated in code anymore
& (
{
kind: 'user';
name: string;
avatarUrl: string;
} | { kind: 'anonym'; }
)
type IUser = {
version: 1;
permissions: ('view' | 'create' | 'edit')[];
// ...
} | {
version: 2;
permissions: {
view: boolean;
create: boolean;
edit: boolean;
};
// ...
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment