type StaticValueType = string | number | boolean; type StringValueType = 'string' | 'number' | 'boolean'; function parse(val: string): 'string'; function parse(val: number): 'number'; function parse(val: boolean): 'boolean'; function parse(val: null, valType: T): T; function parse(val: StaticValueType | null, valType?: StringValueType): StringValueType { if (val == null) { if (valType == null) { throw new Error(); } return valType; } const t = typeof val; switch (t) { case 'boolean': case 'number': case 'string': // TODO: Ideally, this cast wouldn't be necessary! // It ties this code to the StringValueType type union. // It'd be nice to statically verify we've exhaustively checked. return t as StringValueType; default: throw new Error('Should never happen!'); } } // These should not compile (but undefined and null do when `--strictNullChecks` isn't enabled): parse(); parse(undefined); parse(null); parse({}); parse([]); parse(parse); // These should compile, and the type should be correctly inferred! let strType = parse(''); let numType = parse(0); let boolType = parse(false); let verifyStrType: 'string' = strType; let verifyNumType: 'number' = numType; let verifyBoolType: 'boolean' = boolType; // These should not: strType = parse(0); numType = parse(false); boolType = parse(''); // These should compile, and the type should be correctly inferred too! strType = parse(null, 'string'); numType = parse(null, 'number'); boolType = parse(null, 'boolean'); verifyStrType = strType; verifyNumType = numType; verifyBoolType = boolType; // These should not: strType = parse(null, 'boolean'); numType = parse(null, 'string'); boolType = parse(null, 'number');