Last active
March 8, 2021 22:04
-
-
Save toriningen/413c4c8564a56913c0619a534e89d1eb to your computer and use it in GitHub Desktop.
Implementation of Exact types in Typescript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// (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