Skip to content

Instantly share code, notes, and snippets.

@Hadaward
Last active December 15, 2023 17:52
Show Gist options
  • Save Hadaward/7a25306c94b90bcfed9cc52161253324 to your computer and use it in GitHub Desktop.
Save Hadaward/7a25306c94b90bcfed9cc52161253324 to your computer and use it in GitHub Desktop.
typeOf and typeCheck implementation for TypeScript. Now you can ensure your typings on runtime πŸ˜„
const types: Readonly<{[K: string]: any}> = Object.freeze({
NaN: 'nan',
String: 'string',
Number: 'number',
Boolean: 'boolean',
Object: 'object',
Undefined: 'undefined',
Null: 'null',
Function: 'function',
Class: 'class',
});
interface Type {
NaN: number,
String: string,
Number: number,
Boolean: boolean,
Object: object,
Undefined: undefined,
Null: null,
Function: (...args: any[]) => any,
Class: new (...args: any[]) => any,
}
export function typeOf<K extends keyof Type>(value: Type[K]) {
if (typeof value === 'number' && Number.isNaN(value)) {
return 'nan';
}
if (typeof value === 'object') {
if (value === null) {
return 'null';
} else {
return 'object';
}
}
if (typeof value === 'function') {
if (typeof value.prototype !== 'object' || typeof value.prototype.constructor !== 'function') {
return 'function'
}
if (!value.toString().startsWith('class')) {
return 'function';
}
return 'class';
}
return typeof value;
}
export function typeCheck<K extends keyof Type>(typeName: K, value: Type[K]) {
if (typeof types[String(typeName)] !== 'string') {
throw new TypeError(`Invalid type name: ${String(typeName)}`);
}
const realType = types[String(typeName)];
if (typeof value === 'number' && Number.isNaN(value)) {
if (realType !== 'nan') {
throw new TypeError(`${String(value)} expected to be a ${realType} but got NaN (Not a Number).`);
} else {
return;
}
}
if (typeof value === 'object') {
if (realType === 'object' && value === null) {
throw new TypeError(`${String(value)} expected to be a ${realType} but got null.`);
} else if (realType === 'null' && value !== null) {
throw new TypeError(`${String(value)} expected to be a ${realType} but got object.`);
} else {
return;
}
}
if (realType === 'class') {
if (typeof value !== 'function' || typeof value.prototype !== 'object' || typeof value.prototype.constructor !== 'function') {
throw new TypeError(`${String(value)} expected to be a ${realType} but got ${typeof value}.`);
}
if (!value.toString().startsWith('class')) {
throw new TypeError(`${String(value)} expected to be a ${realType} but got ${typeof value}.`);
}
return;
}
if (typeof value !== realType) {
throw new TypeError(`${(value as unknown as Function)?.name ?? String(value)} expected to be of type ${realType} but got ${typeof value}`);
}
}
console.log(typeOf(1));
console.log(typeOf(NaN));
console.log(typeOf(() => {}));
console.log(typeOf(class {}));
console.log(typeOf(null));
console.log(typeOf({}));
console.log(typeOf('foo'));
console.log(typeOf(false));
console.log(typeOf(undefined));
/**
[LOG]: "number"
[LOG]: "nan"
[LOG]: "function"
[LOG]: "class"
[LOG]: "null"
[LOG]: "object"
[LOG]: "string"
[LOG]: "boolean"
[LOG]: "undefined"
**/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment