objcjs-types 0.5.3 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bind.d.ts CHANGED
@@ -7,6 +7,11 @@
7
7
  * @internal
8
8
  */
9
9
  import type { NobjcObject } from "objc-js";
10
+ /**
11
+ * Cached `isKindOfClass:` check — the single path used by both
12
+ * `instanceof` (via `Symbol.hasInstance`) and the `isKindOfClass()` helper.
13
+ */
14
+ export declare function _isKindOfClass(obj: any, cls: any): boolean;
10
15
  /**
11
16
  * Type guard that narrows an ObjC object to a specific class type using
12
17
  * the Objective-C runtime's `-[NSObject isKindOfClass:]` check.
package/dist/bind.js CHANGED
@@ -19,7 +19,7 @@ const _kindCache = new WeakMap();
19
19
  * Cached `isKindOfClass:` check — the single path used by both
20
20
  * `instanceof` (via `Symbol.hasInstance`) and the `isKindOfClass()` helper.
21
21
  */
22
- function _isKindOfClass(obj, cls) {
22
+ export function _isKindOfClass(obj, cls) {
23
23
  if (typeof obj !== "object" || obj === null)
24
24
  return false;
25
25
  let m = _kindCache.get(obj);
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./structs/index.js";
2
2
  export { createDelegate } from "./delegates.js";
3
+ export { defineSubclass, callSuper } from "./subclass.js";
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  // AUTO-GENERATED by objcjs-types generator — DO NOT EDIT
2
2
  export * from "./structs/index.js";
3
3
  export { createDelegate } from "./delegates.js";
4
+ export { defineSubclass, callSuper } from "./subclass.js";
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Type-safe wrapper around NobjcClass.define() for creating Objective-C
3
+ * subclasses at runtime with full TypeScript type inference.
4
+ *
5
+ * Provides autocomplete for protocol method names, types `self` as the
6
+ * superclass instance type, and returns the new class typed as the
7
+ * superclass so that alloc/init and inherited methods are available.
8
+ */
9
+ import type { NobjcObject } from "objc-js";
10
+ import type { ProtocolMap } from "./delegates.js";
11
+ /** Converts a union type to an intersection type. */
12
+ type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
13
+ /**
14
+ * A method definition for a subclass.
15
+ *
16
+ * @param TSelf - The type of `self` (the instance) passed as the first argument
17
+ */
18
+ export interface SubclassMethodDef<TSelf = NobjcObject> {
19
+ /**
20
+ * Objective-C type encoding string.
21
+ *
22
+ * Common encodings:
23
+ * - `@` = id (object), `:` = SEL, `v` = void, `B` = BOOL
24
+ * - `q` = NSInteger (64-bit), `Q` = NSUInteger, `d` = double
25
+ * - `^@` = id* (pointer to object, e.g. NSError**)
26
+ * - `@?` = block
27
+ *
28
+ * Format: `return self _cmd arg1 arg2 ...`
29
+ *
30
+ * @example "v@:" — void method with no args (self + _cmd only)
31
+ * @example "@@:@" — returns object, takes one object arg
32
+ */
33
+ types: string;
34
+ /** The implementation. First argument is always `self` (the instance). */
35
+ implementation: (self: TSelf, ...args: any[]) => any;
36
+ }
37
+ /**
38
+ * Extracts method keys from protocol types for autocomplete.
39
+ * When multiple protocols are specified, collects keys from all of them.
40
+ */
41
+ type ProtocolMethodKeys<TProtocols extends keyof ProtocolMap> = Extract<keyof UnionToIntersection<ProtocolMap[TProtocols]>, string>;
42
+ /**
43
+ * Methods record for a subclass definition.
44
+ *
45
+ * Provides autocomplete for method names from the specified protocols
46
+ * while allowing arbitrary method names for custom methods or
47
+ * superclass overrides.
48
+ */
49
+ type SubclassMethods<TSelf, TProtocols extends keyof ProtocolMap> = {
50
+ [key: string]: SubclassMethodDef<TSelf> | undefined;
51
+ } & {
52
+ [K in ProtocolMethodKeys<TProtocols>]?: SubclassMethodDef<TSelf>;
53
+ };
54
+ /**
55
+ * Define a new Objective-C subclass at runtime with TypeScript type safety.
56
+ *
57
+ * The returned class object is typed as the superclass, so all inherited
58
+ * static methods (`alloc`, `new`, etc.) and instance methods are available.
59
+ * The new class also supports `instanceof` checks via `Symbol.hasInstance`.
60
+ *
61
+ * @param superclass - The superclass to extend (e.g. `NSObject`, `NSViewController`)
62
+ * @param definition - The class definition: name, optional protocols, and method implementations
63
+ * @returns The new ObjC class, typed as the superclass
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * import { NSObject } from "objcjs-types/Foundation";
68
+ * import { defineSubclass, callSuper } from "objcjs-types/subclass";
69
+ *
70
+ * const MyDelegate = defineSubclass(NSObject, {
71
+ * name: "MyDelegate",
72
+ * protocols: ["NSWindowDelegate"],
73
+ * methods: {
74
+ * windowDidResize$: {
75
+ * types: "v@:@",
76
+ * implementation(self, notification) {
77
+ * console.log("Resized!");
78
+ * },
79
+ * },
80
+ * init: {
81
+ * types: "@@:",
82
+ * implementation(self) {
83
+ * return callSuper(self, "init");
84
+ * },
85
+ * },
86
+ * },
87
+ * });
88
+ *
89
+ * const instance = MyDelegate.alloc().init();
90
+ * ```
91
+ */
92
+ export declare function defineSubclass<TClass extends abstract new (...args: any) => any, TProtocols extends keyof ProtocolMap = never>(superclass: TClass, definition: {
93
+ /** Unique name for the new ObjC class (must not collide with existing runtime classes). */
94
+ name: string;
95
+ /** Protocol names to conform to (provides autocomplete for method names). */
96
+ protocols?: TProtocols[];
97
+ /** Instance method implementations. Keys are selector names using `$` for `:`. */
98
+ methods?: SubclassMethods<InstanceType<TClass>, TProtocols>;
99
+ /** Class (static) method implementations. */
100
+ classMethods?: Record<string, SubclassMethodDef>;
101
+ }): TClass;
102
+ /**
103
+ * Call the superclass implementation of a method from within a subclass
104
+ * method implementation.
105
+ *
106
+ * @param self - The instance (`self`, the first argument of your implementation)
107
+ * @param selector - The method selector (e.g. `"init"`, `"viewDidLoad"`)
108
+ * @param args - Arguments to forward to the super implementation
109
+ * @returns The result of the super call
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * init: {
114
+ * types: "@@:",
115
+ * implementation(self) {
116
+ * return callSuper(self, "init");
117
+ * },
118
+ * }
119
+ * ```
120
+ */
121
+ export declare function callSuper(self: NobjcObject, selector: string, ...args: unknown[]): any;
122
+ export {};
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Type-safe wrapper around NobjcClass.define() for creating Objective-C
3
+ * subclasses at runtime with full TypeScript type inference.
4
+ *
5
+ * Provides autocomplete for protocol method names, types `self` as the
6
+ * superclass instance type, and returns the new class typed as the
7
+ * superclass so that alloc/init and inherited methods are available.
8
+ */
9
+ import { NobjcClass } from "objc-js";
10
+ import { _isKindOfClass } from "./bind.js";
11
+ /**
12
+ * Define a new Objective-C subclass at runtime with TypeScript type safety.
13
+ *
14
+ * The returned class object is typed as the superclass, so all inherited
15
+ * static methods (`alloc`, `new`, etc.) and instance methods are available.
16
+ * The new class also supports `instanceof` checks via `Symbol.hasInstance`.
17
+ *
18
+ * @param superclass - The superclass to extend (e.g. `NSObject`, `NSViewController`)
19
+ * @param definition - The class definition: name, optional protocols, and method implementations
20
+ * @returns The new ObjC class, typed as the superclass
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * import { NSObject } from "objcjs-types/Foundation";
25
+ * import { defineSubclass, callSuper } from "objcjs-types/subclass";
26
+ *
27
+ * const MyDelegate = defineSubclass(NSObject, {
28
+ * name: "MyDelegate",
29
+ * protocols: ["NSWindowDelegate"],
30
+ * methods: {
31
+ * windowDidResize$: {
32
+ * types: "v@:@",
33
+ * implementation(self, notification) {
34
+ * console.log("Resized!");
35
+ * },
36
+ * },
37
+ * init: {
38
+ * types: "@@:",
39
+ * implementation(self) {
40
+ * return callSuper(self, "init");
41
+ * },
42
+ * },
43
+ * },
44
+ * });
45
+ *
46
+ * const instance = MyDelegate.alloc().init();
47
+ * ```
48
+ */
49
+ export function defineSubclass(superclass, definition) {
50
+ const methods = {};
51
+ if (definition.methods) {
52
+ for (const [key, def] of Object.entries(definition.methods)) {
53
+ if (def)
54
+ methods[key] = def;
55
+ }
56
+ }
57
+ const classMethods = {};
58
+ if (definition.classMethods) {
59
+ for (const [key, def] of Object.entries(definition.classMethods)) {
60
+ if (def)
61
+ classMethods[key] = def;
62
+ }
63
+ }
64
+ const cls = NobjcClass.define({
65
+ name: definition.name,
66
+ superclass: superclass,
67
+ protocols: definition.protocols,
68
+ methods,
69
+ classMethods: Object.keys(classMethods).length > 0 ? classMethods : undefined
70
+ });
71
+ // Patch Symbol.hasInstance so `instanceof` works with the new class,
72
+ // using the same cached isKindOfClass$ check as _bindClass.
73
+ Object.defineProperty(cls, Symbol.hasInstance, {
74
+ value: (obj) => _isKindOfClass(obj, cls)
75
+ });
76
+ return cls;
77
+ }
78
+ /**
79
+ * Call the superclass implementation of a method from within a subclass
80
+ * method implementation.
81
+ *
82
+ * @param self - The instance (`self`, the first argument of your implementation)
83
+ * @param selector - The method selector (e.g. `"init"`, `"viewDidLoad"`)
84
+ * @param args - Arguments to forward to the super implementation
85
+ * @returns The result of the super call
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * init: {
90
+ * types: "@@:",
91
+ * implementation(self) {
92
+ * return callSuper(self, "init");
93
+ * },
94
+ * }
95
+ * ```
96
+ */
97
+ export function callSuper(self, selector, ...args) {
98
+ return NobjcClass.super(self, selector, ...args);
99
+ }
@@ -2134,6 +2134,7 @@ export function emitTopLevelIndex(frameworkNames: string[]): string {
2134
2134
 
2135
2135
  lines.push(`export * from "./structs/index.js";`);
2136
2136
  lines.push(`export { createDelegate } from "./delegates.js";`);
2137
+ lines.push(`export { defineSubclass, callSuper } from "./subclass.js";`);
2137
2138
  lines.push("");
2138
2139
 
2139
2140
  return lines.join("\n");
@@ -23,7 +23,7 @@ const _kindCache = new WeakMap<object, Map<object, boolean>>();
23
23
  * Cached `isKindOfClass:` check — the single path used by both
24
24
  * `instanceof` (via `Symbol.hasInstance`) and the `isKindOfClass()` helper.
25
25
  */
26
- function _isKindOfClass(obj: any, cls: any): boolean {
26
+ export function _isKindOfClass(obj: any, cls: any): boolean {
27
27
  if (typeof obj !== "object" || obj === null) return false;
28
28
  let m = _kindCache.get(obj);
29
29
  if (m) {
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Type-safe wrapper around NobjcClass.define() for creating Objective-C
3
+ * subclasses at runtime with full TypeScript type inference.
4
+ *
5
+ * Provides autocomplete for protocol method names, types `self` as the
6
+ * superclass instance type, and returns the new class typed as the
7
+ * superclass so that alloc/init and inherited methods are available.
8
+ */
9
+
10
+ import { NobjcClass } from "objc-js";
11
+ import type { NobjcObject } from "objc-js";
12
+ import { _isKindOfClass } from "./bind.js";
13
+ import type { ProtocolMap } from "./delegates.js";
14
+
15
+ /** Converts a union type to an intersection type. */
16
+ type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
17
+
18
+ /**
19
+ * A method definition for a subclass.
20
+ *
21
+ * @param TSelf - The type of `self` (the instance) passed as the first argument
22
+ */
23
+ export interface SubclassMethodDef<TSelf = NobjcObject> {
24
+ /**
25
+ * Objective-C type encoding string.
26
+ *
27
+ * Common encodings:
28
+ * - `@` = id (object), `:` = SEL, `v` = void, `B` = BOOL
29
+ * - `q` = NSInteger (64-bit), `Q` = NSUInteger, `d` = double
30
+ * - `^@` = id* (pointer to object, e.g. NSError**)
31
+ * - `@?` = block
32
+ *
33
+ * Format: `return self _cmd arg1 arg2 ...`
34
+ *
35
+ * @example "v@:" — void method with no args (self + _cmd only)
36
+ * @example "@@:@" — returns object, takes one object arg
37
+ */
38
+ types: string;
39
+ /** The implementation. First argument is always `self` (the instance). */
40
+ implementation: (self: TSelf, ...args: any[]) => any;
41
+ }
42
+
43
+ /**
44
+ * Extracts method keys from protocol types for autocomplete.
45
+ * When multiple protocols are specified, collects keys from all of them.
46
+ */
47
+ type ProtocolMethodKeys<TProtocols extends keyof ProtocolMap> = Extract<
48
+ keyof UnionToIntersection<ProtocolMap[TProtocols]>,
49
+ string
50
+ >;
51
+
52
+ /**
53
+ * Methods record for a subclass definition.
54
+ *
55
+ * Provides autocomplete for method names from the specified protocols
56
+ * while allowing arbitrary method names for custom methods or
57
+ * superclass overrides.
58
+ */
59
+ type SubclassMethods<TSelf, TProtocols extends keyof ProtocolMap> = {
60
+ [key: string]: SubclassMethodDef<TSelf> | undefined;
61
+ } & { [K in ProtocolMethodKeys<TProtocols>]?: SubclassMethodDef<TSelf> };
62
+
63
+ /**
64
+ * Define a new Objective-C subclass at runtime with TypeScript type safety.
65
+ *
66
+ * The returned class object is typed as the superclass, so all inherited
67
+ * static methods (`alloc`, `new`, etc.) and instance methods are available.
68
+ * The new class also supports `instanceof` checks via `Symbol.hasInstance`.
69
+ *
70
+ * @param superclass - The superclass to extend (e.g. `NSObject`, `NSViewController`)
71
+ * @param definition - The class definition: name, optional protocols, and method implementations
72
+ * @returns The new ObjC class, typed as the superclass
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * import { NSObject } from "objcjs-types/Foundation";
77
+ * import { defineSubclass, callSuper } from "objcjs-types/subclass";
78
+ *
79
+ * const MyDelegate = defineSubclass(NSObject, {
80
+ * name: "MyDelegate",
81
+ * protocols: ["NSWindowDelegate"],
82
+ * methods: {
83
+ * windowDidResize$: {
84
+ * types: "v@:@",
85
+ * implementation(self, notification) {
86
+ * console.log("Resized!");
87
+ * },
88
+ * },
89
+ * init: {
90
+ * types: "@@:",
91
+ * implementation(self) {
92
+ * return callSuper(self, "init");
93
+ * },
94
+ * },
95
+ * },
96
+ * });
97
+ *
98
+ * const instance = MyDelegate.alloc().init();
99
+ * ```
100
+ */
101
+ export function defineSubclass<
102
+ TClass extends abstract new (...args: any) => any,
103
+ TProtocols extends keyof ProtocolMap = never
104
+ >(
105
+ superclass: TClass,
106
+ definition: {
107
+ /** Unique name for the new ObjC class (must not collide with existing runtime classes). */
108
+ name: string;
109
+ /** Protocol names to conform to (provides autocomplete for method names). */
110
+ protocols?: TProtocols[];
111
+ /** Instance method implementations. Keys are selector names using `$` for `:`. */
112
+ methods?: SubclassMethods<InstanceType<TClass>, TProtocols>;
113
+ /** Class (static) method implementations. */
114
+ classMethods?: Record<string, SubclassMethodDef>;
115
+ }
116
+ ): TClass {
117
+ type RawMethodDef = { types: string; implementation: (self: NobjcObject, ...args: any[]) => any };
118
+ const methods: Record<string, RawMethodDef> = {};
119
+ if (definition.methods) {
120
+ for (const [key, def] of Object.entries(definition.methods)) {
121
+ if (def) methods[key] = def as RawMethodDef;
122
+ }
123
+ }
124
+
125
+ const classMethods: Record<string, RawMethodDef> = {};
126
+ if (definition.classMethods) {
127
+ for (const [key, def] of Object.entries(definition.classMethods)) {
128
+ if (def) classMethods[key] = def as RawMethodDef;
129
+ }
130
+ }
131
+
132
+ const cls = NobjcClass.define({
133
+ name: definition.name,
134
+ superclass: superclass as unknown as NobjcObject,
135
+ protocols: definition.protocols as string[] | undefined,
136
+ methods,
137
+ classMethods: Object.keys(classMethods).length > 0 ? classMethods : undefined
138
+ });
139
+
140
+ // Patch Symbol.hasInstance so `instanceof` works with the new class,
141
+ // using the same cached isKindOfClass$ check as _bindClass.
142
+ Object.defineProperty(cls, Symbol.hasInstance, {
143
+ value: (obj: any) => _isKindOfClass(obj, cls)
144
+ });
145
+
146
+ return cls as unknown as TClass;
147
+ }
148
+
149
+ /**
150
+ * Call the superclass implementation of a method from within a subclass
151
+ * method implementation.
152
+ *
153
+ * @param self - The instance (`self`, the first argument of your implementation)
154
+ * @param selector - The method selector (e.g. `"init"`, `"viewDidLoad"`)
155
+ * @param args - Arguments to forward to the super implementation
156
+ * @returns The result of the super call
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * init: {
161
+ * types: "@@:",
162
+ * implementation(self) {
163
+ * return callSuper(self, "init");
164
+ * },
165
+ * }
166
+ * ```
167
+ */
168
+ export function callSuper(self: NobjcObject, selector: string, ...args: unknown[]): any {
169
+ return NobjcClass.super(self, selector, ...args);
170
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "objcjs-types",
3
- "version": "0.5.3",
3
+ "version": "0.6.3",
4
4
  "description": "Auto-generated TypeScript type declarations for macOS Objective-C frameworks that supplement objc-js",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -27,6 +27,10 @@
27
27
  "types": "./dist/osversion.d.ts",
28
28
  "default": "./dist/osversion.js"
29
29
  },
30
+ "./subclass": {
31
+ "types": "./dist/subclass.d.ts",
32
+ "default": "./dist/subclass.js"
33
+ },
30
34
  "./*": {
31
35
  "types": "./dist/*/index.d.ts",
32
36
  "default": "./dist/*/index.js"