Skip to content

Instantly share code, notes, and snippets.

@toriningen
Last active March 8, 2021 22:04
Show Gist options
  • Save toriningen/413c4c8564a56913c0619a534e89d1eb to your computer and use it in GitHub Desktop.
Save toriningen/413c4c8564a56913c0619a534e89d1eb to your computer and use it in GitHub Desktop.
Implementation of Exact types in Typescript
// (these two types MUST NOT be merged into a single declaration)
type ExactInner<T> = <D>() => (D extends T ? D : D);
type Exact<T> = ExactInner<T> & T;
type Unexact<T> = T extends Exact<infer R> ? R : T;
function exact<T>(obj: Exact<T> | T): Exact<T> {
return obj as Exact<T>;
};
////////////////////////////////
// Fixtures:
type Wide = { foo: string, bar: string };
type Narrow = { foo: string };
type ExactWide = Exact<Wide>;
type ExactNarrow = Exact<Narrow>;
type FW = Wide;
type FN = Narrow;
type EW = ExactWide;
type EN = ExactNarrow;
declare const fw: FW;
declare const fn: FN;
declare const ew: EW;
declare const en: EN;
////////////////////////////////
// Tests:
const assign_fw_fw: FW = fw; // OK, rtype extends ltype
/* !!! */ const assign_fw_fn: FW = fn; // ERR, rtype DOES NOT EXTEND ltype
const assign_fw_ew: FW = ew; // OK, rtype extends ltype
/* !!! */ const assign_fw_en: FW = en; // ERR, rtype DOES NOT EXTEND ltype
const assign_fn_fw: FN = fw; // OK, rtype extends ltype
const assign_fn_fn: FN = fn; // OK, rtype extends ltype
const assign_fn_ew: FN = ew; // OK, rtype extends ltype
const assign_fn_en: FN = en; // OK, rtype extends ltype
/* !!! */ const assign_ew_fw: EW = fw; // ERR, no implicit restricting
/* !!! */ const assign_ew_fn: EW = fn; // ERR, no implicit restricting
const assign_ew_ew: EW = ew; // OK, rtype is ltype
/* !!! */ const assign_ew_en: EW = en; // ERR, rtype IS NOT ltype
/* !!! */ const assign_en_fw: EN = fw; // ERR, no implicit restricting
/* !!! */ const assign_en_fn: EN = fn; // ERR, no implicit restricting
/* !!! */ const assign_en_ew: EN = ew; // ERR, rtype IS NOT ltype
const assign_en_en: EN = en; // OK, rtype is ltype
const convert_fw_fw: FW = exact(fw); // OK, rtype extends ltype
/* !!! */ const convert_fw_fn: FW = exact(fn); // ERR, rtype DOES NOT EXTEND ltype
const convert_fw_ew: FW = exact(ew); // OK, rtype extends ltype
/* !!! */ const convert_fw_en: FW = exact(en); // ERR, rtype DOES NOT EXTEND ltype
const convert_fn_fw: FN = exact(fw); // OK, rtype extends ltype
const convert_fn_fn: FN = exact(fn); // OK, rtype extends ltype
const convert_fn_ew: FN = exact(ew); // OK, rtype extends ltype
const convert_fn_en: FN = exact(en); // OK, rtype extends ltype
const convert_ew_fw: EW = exact(fw); // OK, rtype extends ltype, rtype is ltype
/* !!! */ const convert_ew_fn: EW = exact(fn); // ERR, rtype DOES NOT EXTEND ltype
const convert_ew_ew: EW = exact(ew); // OK, rtype is ltype
/* !!! */ const convert_ew_en: EW = exact(en); // ERR, rtype IS NOT ltype
/* !!! */ const convert_en_fw: EN = exact(fw); // ERR, rtype extends ltype, but rtype IS NOT ltype
const convert_en_fn: EN = exact(fn); // OK, rtype extends ltype, rtype is ltype
/* !!! */ const convert_en_ew: EN = exact(ew); // ERR, rtype IS NOT ltype
const convert_en_en: EN = exact(en); // OK, rtype is ltype
/* call_fw_fw */ ((fw: FW) => 0)(fw); // OK, argtype extends partype
/* !!! */ /* call_fw_fn */ ((fw: FW) => 0)(fn); // ERR, argtype DOES NOT EXTEND partype
/* call_fw_ew */ ((fw: FW) => 0)(ew); // OK, argtype extends partype
/* !!! */ /* call_fw_en */ ((fw: FW) => 0)(en); // ERR, argtype DOES NOT EXTEND partype
/* call_fn_fw */ ((fn: FN) => 0)(fw); // OK, argtype extends partype
/* call_fn_fn */ ((fn: FN) => 0)(fn); // OK, argtype extends partype
/* call_fn_ew */ ((fn: FN) => 0)(ew); // OK, argtype extends partype
/* call_fn_en */ ((fn: FN) => 0)(en); // OK, argtype extends partype
/* !!! */ /* call_ew_fw */ ((ew: EW) => 0)(fw); // ERR, no implicit restricting
/* !!! */ /* call_ew_fn */ ((ew: EW) => 0)(fn); // ERR, no implicit restricting
/* call_ew_ew */ ((ew: EW) => 0)(ew); // OK, argtype is partype
/* !!! */ /* call_ew_en */ ((ew: EW) => 0)(en); // ERR, argtype IS NOT partype
/* !!! */ /* call_en_fw */ ((en: EN) => 0)(fw); // ERR, no implicit restricting
/* !!! */ /* call_en_fn */ ((en: EN) => 0)(fn); // ERR, no implicit restricting
/* !!! */ /* call_en_ew */ ((en: EN) => 0)(ew); // ERR, argtype IS NOT partype
/* call_en_en */ ((en: EN) => 0)(en); // OK, argtype is partype
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment