diff --git a/package.json b/package.json
index 4fe9c55..04c08ce 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "spectacles-ts",
- "version": "1.0.6",
+ "version": "1.0.7",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"sideEffects": false,
@@ -16,7 +16,7 @@
"watch:tsc": "yarn run tsc -w -p tsconfig.build.json",
"watch:tsd": "nodemon --watch './**/*.ts' --ignore './dist' --exec 'ts-node' --transpile-only scripts/run-type-tests.ts",
"fix:lint": "yarn run eslint . --fix",
- "prepublishOnly": "yarn fix:lint && yarn test"
+ "prepublishOnly": "yarn fix:lint && yarn build && yarn test"
},
"dependencies": {
"monocle-ts": "^2.3.5"
@@ -24,6 +24,7 @@
"devDependencies": {
"@types/jest": "^26.0.20",
"@types/node": "^14.14.37",
+ "@types/react": "^18.0.1",
"@typescript-eslint/eslint-plugin": "^5.16.0",
"@typescript-eslint/parser": "^5.16.0",
"assert": "^2.0.0",
diff --git a/src/util/Paths.ts b/src/util/Paths.ts
index dd70831..56e10aa 100644
--- a/src/util/Paths.ts
+++ b/src/util/Paths.ts
@@ -1,59 +1,80 @@
import type { Option, Some } from "fp-ts/Option";
import type { Either, Left, Right } from "fp-ts/Either";
-import type { IsNull, IsRecord, TupleKeyof } from "./predicates";
+import type { IsAny, IsNull, IsRecord, TupleKeyof } from "./predicates";
import type { EscapeSpecialChars } from "./segments";
import type { Cases, Discriminant } from "./sum";
+import { B_extends_A, GetParentInterfaces, NewRec } from "./pathRecursion";
type Operation = "static" | "dynamic" | "upsert";
-export type Paths = _Paths<{ "": A }, Op>;
+export type Paths = _Paths;
-type _Paths = true extends IsRecord
- ? _Paths, Op, Acc | Extract>
+export type _Paths<
+ A,
+ Recursed,
+ Op extends Operation = "static",
+ Acc extends string = never,
+ It extends unknown[] = []
+> = /* It["length"] extends 3
+ ? {
+ A: A;
+ Recursed: Recursed;
+ Acc: Acc;
+ }
+ : */ true extends IsRecord
+ ? _Paths<
+ keyof A extends never ? unknown : BubbleUp,
+ NewRec,
+ Op,
+ Acc | Extract,
+ [...It, unknown]
+ >
: Acc;
-type BubbleUp> = UnionToIntersection>>;
+export type BubbleUp, Recursed> = UnionToIntersection>>;
-type _BubbleUp> = {
- [K in keyof A]-?: Match<
- A[K],
- {
- nullable: Record<`${Extract}?`, NonNullable>;
- struct: {
- [K2 in keyof A[K] as `${Extract}${Extract extends "" ? "" : "."}${EscapeSpecialChars<
- Extract
- >}`]: A[K][K2];
- };
- tuple: {
- [K2 in TupleKeyof as `${Extract}${Extract extends "" ? "" : "."}[${Extract<
- K2,
- string
- >}]`]: A[K][K2];
- };
- record: Record<
- `${Extract}${Extract extends "" ? "" : "."}${"[string]" | "{}>"}`,
- A[K][string]
- >;
- array: Record<
- `${Extract}${Extract extends "" ? "" : "."}${"[number]" | "[]>"}`,
- A[K][number]
- >;
- option: Record<
- `${Extract}${Extract extends "" ? "" : "."}?some`,
- Extract>["value"]
+export type _BubbleUp, Recursed> = {
+ [K in keyof A]-?: true extends B_extends_A>
+ ? never
+ : Match<
+ A[K],
+ {
+ nullable: { [K1 in `${Extract}?`]: NonNullable };
+ struct: {
+ [K2 in keyof A[K] as `${Extract}${Extract extends "" ? "" : "."}${EscapeSpecialChars<
+ Extract
+ >}`]: A[K][K2];
+ };
+ tuple: {
+ [K2 in TupleKeyof as `${Extract}${Extract extends "" ? "" : "."}[${Extract<
+ K2,
+ string
+ >}]`]: A[K][K2];
+ };
+ record: Record<
+ `${Extract}${Extract extends "" ? "" : "."}${"[string]" | "{}>"}`,
+ A[K][string]
+ >;
+ array: Record<
+ `${Extract}${Extract extends "" ? "" : "."}${"[number]" | "[]>"}`,
+ A[K][number]
+ >;
+ option: Record<
+ `${Extract}${Extract extends "" ? "" : "."}?some`,
+ Extract>["value"]
+ >;
+ either: Record<
+ `${Extract}${Extract extends "" ? "" : "."}?left`,
+ Extract>["left"]
+ > &
+ Record<
+ `${Extract}${Extract extends "" ? "" : "."}?right`,
+ Extract>["right"]
+ >;
+ sum: BubbleSum>;
+ other: never;
+ }
>;
- either: Record<
- `${Extract}${Extract extends "" ? "" : "."}?left`,
- Extract>["left"]
- > &
- Record<
- `${Extract}${Extract extends "" ? "" : "."}?right`,
- Extract>["right"]
- >;
- sum: BubbleSum>;
- other: never;
- }
- >;
};
type BubbleSum<
@@ -84,7 +105,13 @@ type Match<
sum: unknown;
other: unknown;
}
-> = [A] extends [never]
+> = unknown extends A
+ ? Matches["other"]
+ : IsAny extends true
+ ? Matches["other"]
+ : [A] extends [never]
+ ? Matches["other"]
+ : A extends (...a: any[]) => unknown
? Matches["other"]
: true extends IsNull
? Matches["nullable"]
@@ -106,6 +133,6 @@ type Match<
// Credit to Stefan Baumgartner
// https://fettblog.eu/typescript-union-to-intersection/
-type UnionToIntersection = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never;
+export type UnionToIntersection = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never;
-type ValueOf = A[keyof A];
+export type ValueOf = A[keyof A];
diff --git a/src/util/pathRecursion.ts b/src/util/pathRecursion.ts
new file mode 100644
index 0000000..974ebe8
--- /dev/null
+++ b/src/util/pathRecursion.ts
@@ -0,0 +1,56 @@
+import type { Option } from "fp-ts/Option";
+import type { Either } from "fp-ts/Either";
+import type { IsAny, IsNull, IsRecord, TupleKeyof } from "./predicates";
+
+export type CanRecurse = unknown extends A
+ ? never
+ : IsAny extends true
+ ? never
+ : [A] extends [never]
+ ? never
+ : A extends (...a: any[]) => unknown
+ ? never
+ : true extends IsNull
+ ? never
+ : [A] extends [Option]
+ ? never
+ : [A] extends [Either]
+ ? never
+ : [A] extends [readonly unknown[]]
+ ? TupleKeyof extends never
+ ? never
+ : true
+ : string extends keyof A
+ ? never
+ : true extends IsRecord
+ ? true
+ : never;
+
+export type GetParentInterfaces = ValueOf<{
+ [K in keyof AllParents as Key extends `${Extract}${string}` ? K : never]: AllParents[K];
+}>;
+
+export type NewRec = {
+ [K in keyof A]-?: GetParentInterfaces | (true extends CanRecurse ? A[K] : never);
+};
+
+export type AllKeys = A extends unknown ? keyof A : never;
+
+export type PossiblyExtendible = B extends unknown ? (keyof A extends keyof B ? B : never) : never;
+
+export type UnPartial = {
+ [K in keyof Required]: Required[K] | Extract]>;
+};
+
+// check whether B extends A without putting A in the check type
+export type B_extends_A = true extends CanRecurse
+ ? A extends B
+ ? true
+ : {
+ [K in AllKeys>]: K extends keyof A ? A[K] : any;
+ } extends UnPartial>
+ ? true
+ : never
+ : never;
+
+export type ValueOf = A[keyof A];
diff --git a/src/util/predicates.ts b/src/util/predicates.ts
index 60ea53b..e78673c 100644
--- a/src/util/predicates.ts
+++ b/src/util/predicates.ts
@@ -58,9 +58,13 @@ type HasTraversals = Args extends [infer First, ...infer
: HasTraversals
: never;
+// credit to Joe Calzaretta
+// https://stackoverflow.com/a/49928360
+export type IsAny = (A extends never ? true : false) extends false ? false : true;
+
export type IsNull = Extract extends never ? never : true;
-export type IsRecord = unknown extends A ? never : [A] extends [Record] ? true : never;
+export type IsRecord = [A] extends [Record] ? true : never;
export type IsNonTupleArray = [A] extends [readonly unknown[]]
? TupleKeyof extends never
diff --git a/src/util/scratch.ts b/src/util/scratch.ts
new file mode 100644
index 0000000..f0618d3
--- /dev/null
+++ b/src/util/scratch.ts
@@ -0,0 +1,121 @@
+import { Paths, _Paths, BubbleUp, _BubbleUp, UnionToIntersection, ValueOf } from "./Paths";
+import { AllKeys, B_extends_A, PossiblyExtendible, UnPartial, GetParentInterfaces, NewRec } from "./pathRecursion";
+
+export type Element2 = {
+ readonly ownerDocument: Document;
+};
+
+type b26 = Paths<{ ownerDocument: Document }>;
+
+// declare const yyy: b26["Recursed"];
+
+// const a = yyy["ownerDocument.ownerDocument?"];
+type ab1 = B_extends_A<
+ Document,
+ {
+ ownerDocument: Document;
+ }
+>;
+
+type a = Paths<{ a: { b: { c: number } }; x: { c: number } }>;
+type b = Paths;
+
+interface Rec {
+ a: Rec;
+}
+
+interface Rec2 extends Rec {
+ b: Rec;
+}
+
+declare const rec: Rec2;
+
+rec.a.a.a.a;
+
+type r0 = _Paths, []>;
+
+type bup1 = _BubbleUp;
+
+type bup2 = B_extends_A;
+type bup3 = {
+ [K in AllKeys>]: K extends keyof Rec[] ? Rec[][K] : any;
+};
+type bup4 = UnPartial>;
+
+type r1 = _Paths, "static", Extract>;
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+
+/* type z0 = B_extends_A;
+type z1 = B_extends_A<{ a?: number }, Rec2>;
+type z2 = z0 extends UnPartial ? 1 : 0;
+type z3 = z1 extends UnPartial ? 1 : 0; */
+type z96 = { a: number | undefined } extends { a?: Rec } ? 1 : 0;
+
+type p0 = ` ${keyof HTMLInputElement}`;
+//type b0 = Paths;
+//type b1 = Paths;
+//type b2 = Paths;
+//type b3 = Paths;
+// type b4 = Paths;
+//type b5 = Paths;
+//type b24 = Paths<{ ownerDocument: Document }>;
+// type b25 = Paths;
+// type b6 = Paths;
+// type b7 = Paths;
+// declare const b3: b2;
+declare const doc: Document;
+
+// b3 === "ownerDocument.documentElement";
+
+/* const aaa = b3.Recursed["ownerDocument.activeElement?"]; */
+/*type b5 = Paths;
+
+
+
+const aaa = b5.Recursed["anchors"];*/
+
+/*
+Obj1 = { a: { b: number, x: boolean }, c: { d: { e: string } } }
+Output1 = keyof Obj1 = 'a' | 'c'
+Rec1 = { a: { b: number, x: boolean }, c: { d: { e: string } } }
+ |
+ V
+Obj2 = { 'a.b': number, 'a.x': boolean, 'c.d': { e: string } }
+Output2 = Output1 | keyof Obj2 = 'a' | 'c' | 'a.b' | 'a.x' | 'c.d'
+Rec2 = { 'a.b': { b: number, x: boolean }, 'c.d': { d: { e: string } } }
+ |
+ V
+Obj3 = { 'c.d.e': string }
+Output3 = Output2 | keyof Obj3 = 'a' | 'c' | 'a.b' | 'a.x' | 'c.d' | 'c.d.e'
+Rec3 = { 'c.d.e': { d: { e: string } } | { e: string } | string }
+*/
+
+type rec = { "a.b": { b: number; x: boolean }; "c.d": { d: { e: string } } };
+
+type parens = GetParentInterfaces<"c.d.e", rec>;
+
+type newrec = NewRec;
+
+type a1 = Paths;
+declare const a2: a1;
+
+interface Document2 {
+ readonly documentElement: HTMLElement;
+ // readonly head: HTMLHeadElement;
+ // readonly embeds: HTMLCollectionOf;
+ // readonly forms: HTMLCollectionOf;
+ // readonly images: HTMLCollectionOf;
+ // readonly doctype: DocumentType | null;
+}
+
+// type b27 = Paths<{ ownerDocument: Document2 }>;
+type b28 = Paths;
diff --git a/yarn.lock b/yarn.lock
index f22b89f..2a34a4b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -778,11 +778,30 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.2.tgz#fc8c2825e4ed2142473b4a81064e6e081463d1b3"
integrity sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog==
+"@types/prop-types@*":
+ version "15.7.5"
+ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
+ integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
+
"@types/qs@^6.2.31":
version "6.9.7"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
+"@types/react@^18.0.1":
+ version "18.0.1"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.1.tgz#1b2e02fb7613212518733946e49fb963dfc66e19"
+ integrity sha512-VnWlrVgG0dYt+NqlfMI0yUYb8Rdl4XUROyH+c6gq/iFCiZ805Vi//26UW38DHnxQkbDhnrIWTBiy6oKZqL11cw==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
+"@types/scheduler@*":
+ version "0.16.2"
+ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
+ integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
+
"@types/stack-utils@^2.0.0":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
@@ -1610,6 +1629,11 @@ cssstyle@^2.3.0:
dependencies:
cssom "~0.3.6"
+csstype@^3.0.2:
+ version "3.0.11"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.11.tgz#d66700c5eacfac1940deb4e3ee5642792d85cd33"
+ integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==
+
data-urls@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b"