Skip to content

Commit 74bdd51

Browse files
I think it's all working now
1 parent 8c777a4 commit 74bdd51

File tree

2 files changed

+56
-43
lines changed

2 files changed

+56
-43
lines changed

index.d.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
export declare namespace Base {
2-
interface Options {
3-
version: string;
4-
[key: string]: unknown;
5-
}
2+
interface Options {}
63
}
74

85
declare type ApiExtension = {
@@ -32,22 +29,30 @@ declare type ReturnTypeOf<T extends AnyFunction | AnyFunction[]> =
3229
: never;
3330

3431
type ClassWithPlugins = Constructor<any> & {
35-
plugins: any[];
32+
plugins: Plugin[];
3633
};
3734

35+
type RemainingRequirements<PredefinedOptions> =
36+
keyof PredefinedOptions extends never
37+
? Base.Options
38+
: Omit<Base.Options, keyof PredefinedOptions>
39+
40+
type NonOptionalKeys<Obj> = {
41+
[K in keyof Obj]: {} extends Pick<Obj, K> ? never : K;
42+
}[keyof Obj];
43+
44+
type RequiredIfRemaining<PredefinedOptions, NowProvided> =
45+
NonOptionalKeys<RemainingRequirements<PredefinedOptions>> extends never
46+
? [(Partial<Base.Options> & NowProvided)?]
47+
: [Partial<Base.Options> & RemainingRequirements<PredefinedOptions> & NowProvided];
48+
3849
type ConstructorRequiringVersion<Class extends ClassWithPlugins, PredefinedOptions> = {
3950
defaultOptions: PredefinedOptions;
40-
} & (PredefinedOptions extends { version: string }
41-
? {
42-
new <NowProvided>(options?: NowProvided): Class & {
43-
options: NowProvided & PredefinedOptions;
44-
};
45-
}
46-
: {
47-
new <NowProvided>(options: Base.Options & NowProvided): Class & {
48-
options: NowProvided & PredefinedOptions;
49-
};
50-
});
51+
} & {
52+
new <NowProvided>(...options: RequiredIfRemaining<PredefinedOptions, NowProvided>): Class & {
53+
options: NowProvided & PredefinedOptions;
54+
};
55+
};
5156

5257
export declare class Base<TOptions extends Base.Options = Base.Options> {
5358
static plugins: Plugin[];

index.test-d.ts

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,45 @@ import { barPlugin } from "./plugins/bar/index.js";
66
import { voidPlugin } from "./plugins/void/index.js";
77
import { withOptionsPlugin } from "./plugins/with-options";
88

9-
const base = new Base({
10-
version: "1.2.3",
9+
declare module "./index.js" {
10+
namespace Base {
11+
interface Options {
12+
required: string;
13+
}
14+
}
15+
}
16+
17+
const baseSatisfied = new Base({
18+
required: "1.2.3",
1119
});
1220

1321
// @ts-expect-error unknown properties cannot be used, see #31
14-
base.unknown;
22+
baseSatisfied.unknown;
1523

1624
const BaseWithEmptyDefaults = Base.defaults({
1725
// there should be no required options
1826
});
1927

20-
// 'version' is missing and should still be required
28+
// 'required' is missing and should still be required
2129
// @ts-expect-error
2230
new BaseWithEmptyDefaults();
2331

24-
// 'version' is missing and should still be required
32+
// 'required' is missing and should still be required
2533
// @ts-expect-error
2634
new BaseWithEmptyDefaults({});
2735

2836
const BaseLevelOne = Base.plugin(fooPlugin).defaults({
2937
defaultOne: "value",
30-
version: "1.2.3",
38+
required: "1.2.3",
3139
});
3240

33-
// Because 'version' is already provided, this needs no argument
41+
// Because 'required' is already provided, this needs no argument
3442
new BaseLevelOne();
3543
new BaseLevelOne({});
3644

3745
expectType<{
3846
defaultOne: string;
39-
version: string;
47+
required: string;
4048
}>(BaseLevelOne.defaultOptions);
4149

4250
const baseLevelOne = new BaseLevelOne({
@@ -45,7 +53,7 @@ const baseLevelOne = new BaseLevelOne({
4553

4654
expectType<string>(baseLevelOne.options.defaultOne);
4755
expectType<string>(baseLevelOne.options.optionOne);
48-
expectType<string>(baseLevelOne.options.version);
56+
expectType<string>(baseLevelOne.options.required);
4957
// @ts-expect-error unknown properties cannot be used, see #31
5058
baseLevelOne.unknown;
5159

@@ -56,16 +64,16 @@ const BaseLevelTwo = BaseLevelOne.defaults({
5664
expectType<{
5765
defaultOne: string;
5866
defaultTwo: number;
59-
version: string;
67+
required: string;
6068
}>({ ...BaseLevelTwo.defaultOptions });
6169

62-
// Because 'version' is already provided, this needs no argument
70+
// Because 'required' is already provided, this needs no argument
6371
new BaseLevelTwo();
6472
new BaseLevelTwo({});
6573

66-
// 'version' may be overriden, though it's not necessary
74+
// 'required' may be overriden, though it's not necessary
6775
new BaseLevelTwo({
68-
version: "new version",
76+
required: "new required",
6977
});
7078

7179
const baseLevelTwo = new BaseLevelTwo({
@@ -75,7 +83,7 @@ const baseLevelTwo = new BaseLevelTwo({
7583
expectType<number>(baseLevelTwo.options.defaultTwo);
7684
expectType<string>(baseLevelTwo.options.defaultOne);
7785
expectType<boolean>(baseLevelTwo.options.optionTwo);
78-
expectType<string>(baseLevelTwo.options.version);
86+
expectType<string>(baseLevelTwo.options.required);
7987
// @ts-expect-error unknown properties cannot be used, see #31
8088
baseLevelTwo.unknown;
8189

@@ -87,18 +95,18 @@ expectType<{
8795
defaultOne: string;
8896
defaultTwo: number;
8997
defaultThree: string[];
90-
version: string;
98+
required: string;
9199
}>({ ...BaseLevelThree.defaultOptions });
92100

93-
// Because 'version' is already provided, this needs no argument
101+
// Because 'required' is already provided, this needs no argument
94102
new BaseLevelThree();
95103
new BaseLevelThree({});
96104

97105
// Previous settings may be overriden, though it's not necessary
98106
new BaseLevelThree({
99107
optionOne: "",
100108
optionTwo: false,
101-
version: "new version",
109+
required: "new required",
102110
});
103111

104112
const baseLevelThree = new BaseLevelThree({
@@ -109,21 +117,21 @@ expectType<string>(baseLevelThree.options.defaultOne);
109117
expectType<number>(baseLevelThree.options.defaultTwo);
110118
expectType<string[]>(baseLevelThree.options.defaultThree);
111119
expectType<number[]>(baseLevelThree.options.optionThree);
112-
expectType<string>(baseLevelThree.options.version);
120+
expectType<string>(baseLevelThree.options.required);
113121
// @ts-expect-error unknown properties cannot be used, see #31
114122
baseLevelThree.unknown;
115123

116124
const BaseWithVoidPlugin = Base.plugin(voidPlugin);
117125
const baseWithVoidPlugin = new BaseWithVoidPlugin({
118-
version: "1.2.3",
126+
required: "1.2.3",
119127
});
120128

121129
// @ts-expect-error unknown properties cannot be used, see #31
122130
baseWithVoidPlugin.unknown;
123131

124132
const BaseWithFooAndBarPlugins = Base.plugin(barPlugin, fooPlugin);
125133
const baseWithFooAndBarPlugins = new BaseWithFooAndBarPlugins({
126-
version: "1.2.3",
134+
required: "1.2.3",
127135
});
128136

129137
expectType<string>(baseWithFooAndBarPlugins.foo);
@@ -138,7 +146,7 @@ const BaseWithVoidAndNonVoidPlugins = Base.plugin(
138146
fooPlugin
139147
);
140148
const baseWithVoidAndNonVoidPlugins = new BaseWithVoidAndNonVoidPlugins({
141-
version: "1.2.3",
149+
required: "1.2.3",
142150
});
143151

144152
expectType<string>(baseWithVoidAndNonVoidPlugins.foo);
@@ -149,7 +157,7 @@ baseWithVoidAndNonVoidPlugins.unknown;
149157

150158
const BaseWithOptionsPlugin = Base.plugin(withOptionsPlugin);
151159
const baseWithOptionsPlugin = new BaseWithOptionsPlugin({
152-
version: "1.2.3",
160+
required: "1.2.3",
153161
});
154162

155163
expectType<string>(baseWithOptionsPlugin.getFooOption());
@@ -158,7 +166,7 @@ expectType<string>(baseWithOptionsPlugin.getFooOption());
158166
const BaseLevelFour = BaseLevelThree.defaults({ defaultFour: 4 });
159167

160168
expectType<{
161-
version: string;
169+
required: string;
162170
defaultOne: string;
163171
defaultTwo: number;
164172
defaultThree: string[];
@@ -170,14 +178,14 @@ const baseLevelFour = new BaseLevelFour();
170178
// See the node on static defaults in index.d.ts for why defaultFour is missing
171179
// .options from .defaults() is only supported until a depth of 4
172180
expectType<{
173-
version: string;
181+
required: string;
174182
defaultOne: string;
175183
defaultTwo: number;
176184
defaultThree: string[];
177185
}>({ ...baseLevelFour.options });
178186

179187
expectType<{
180-
version: string;
188+
required: string;
181189
defaultOne: string;
182190
defaultTwo: number;
183191
defaultThree: string[];
@@ -195,7 +203,7 @@ const BaseWithChainedDefaultsAndPlugins = Base.defaults({
195203

196204
const baseWithChainedDefaultsAndPlugins = new BaseWithChainedDefaultsAndPlugins(
197205
{
198-
version: "1.2.3",
206+
required: "1.2.3",
199207
}
200208
);
201209

@@ -221,7 +229,7 @@ expectType<{
221229

222230
const baseWithManyChainedDefaultsAndPlugins =
223231
new BaseWithManyChainedDefaultsAndPlugins({
224-
version: "1.2.3",
232+
required: "1.2.3",
225233
foo: "bar",
226234
});
227235

0 commit comments

Comments
 (0)