Skip to content

Commit d7048d4

Browse files
authored
fix: defineComponent() with array props (#364)
* fix: defineComponent() with array props * fix * fix by @pikax * add test * update test * fix
1 parent bca3a69 commit d7048d4

File tree

5 files changed

+80
-38
lines changed

5 files changed

+80
-38
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"rollup-plugin-terser": "^6.1.0",
5050
"rollup-plugin-typescript2": "^0.27.1",
5151
"ts-jest": "^26.1.0",
52-
"typescript": "^3.9.3",
52+
"typescript": "^3.9.5",
5353
"vue": "^2.6.11",
5454
"vue-router": "^3.3.2",
5555
"vue-server-renderer": "^2.6.11"

src/component/component.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ interface ComponentOptionsWithProps<
6565
setup?: SetupFunction<Props, RawBindings>;
6666
}
6767

68+
interface ComponentOptionsWithArrayProps<
69+
PropNames extends string = string,
70+
RawBindings = Data,
71+
Props = Readonly<{ [key in PropNames]?: any }>
72+
> {
73+
props?: PropNames[];
74+
setup?: SetupFunction<Props, RawBindings>;
75+
}
76+
6877
interface ComponentOptionsWithoutProps<Props = unknown, RawBindings = Data> {
6978
props?: undefined;
7079
setup?: SetupFunction<Props, RawBindings>;
@@ -74,7 +83,20 @@ interface ComponentOptionsWithoutProps<Props = unknown, RawBindings = Data> {
7483
export function defineComponent<RawBindings>(
7584
options: ComponentOptionsWithoutProps<unknown, RawBindings>
7685
): VueProxy<unknown, RawBindings>;
77-
// overload 2: object format with object props declaration
86+
// overload 2: object format with array props declaration
87+
// props inferred as { [key in PropNames]?: any }
88+
// return type is for Vetur and TSX support
89+
export function defineComponent<
90+
PropNames extends string,
91+
RawBindings = Data,
92+
PropsOptions extends ComponentPropsOptions = ComponentPropsOptions
93+
>(
94+
// prettier-ignore
95+
options: (
96+
ComponentOptionsWithArrayProps<PropNames, RawBindings>) &
97+
Omit<Vue2ComponentOptions<Vue>, keyof ComponentOptionsWithProps<never, never>>
98+
): VueProxy<Readonly<{ [key in PropNames]?: any }>, RawBindings>;
99+
// overload 3: object format with object props declaration
78100
// see `ExtractPropTypes` in ./componentProps.ts
79101
export function defineComponent<
80102
Props,
@@ -94,12 +116,24 @@ export function defineComponent(options: any) {
94116
return options as any;
95117
}
96118

97-
// createComponent is kept around for retro-compatibility
98119
// overload 1: object format with no props
99120
export function createComponent<RawBindings>(
100121
options: ComponentOptionsWithoutProps<unknown, RawBindings>
101122
): VueProxy<unknown, RawBindings>;
102-
// overload 2: object format with object props declaration
123+
// overload 2: object format with array props declaration
124+
// props inferred as { [key in PropNames]?: any }
125+
// return type is for Vetur and TSX support
126+
export function createComponent<
127+
PropNames extends string,
128+
RawBindings = Data,
129+
PropsOptions extends ComponentPropsOptions = ComponentPropsOptions
130+
>(
131+
// prettier-ignore
132+
options: (
133+
ComponentOptionsWithArrayProps<PropNames, RawBindings>) &
134+
Omit<Vue2ComponentOptions<Vue>, keyof ComponentOptionsWithProps<never, never>>
135+
): VueProxy<Readonly<{ [key in PropNames]?: any }>, RawBindings>;
136+
// overload 3: object format with object props declaration
103137
// see `ExtractPropTypes` in ./componentProps.ts
104138
export function createComponent<
105139
Props,

src/component/componentProps.ts

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
import { Data } from './component';
22

3-
export type ComponentPropsOptions<P = Data> = {
4-
[K in keyof P]: Prop<P[K], true | false> | null;
3+
export type ComponentPropsOptions<P = Data> = ComponentObjectPropsOptions<P> | string[];
4+
5+
export type ComponentObjectPropsOptions<P = Data> = {
6+
[K in keyof P]: Prop<P[K]> | null;
57
};
68

7-
type Prop<T, Required extends boolean> = PropOptions<T, Required> | PropType<T>;
9+
export type Prop<T> = PropOptions<T> | PropType<T>;
10+
11+
type DefaultFactory<T> = () => T | null | undefined;
812

9-
export interface PropOptions<T = any, Required extends boolean = false> {
10-
type?: PropType<T> | null;
11-
required?: Required;
12-
default?: T | null | undefined | (() => T | null | undefined);
13-
validator?(value: any): boolean;
13+
export interface PropOptions<T = any> {
14+
type?: PropType<T> | true | null;
15+
required?: boolean;
16+
default?: T | DefaultFactory<T> | null | undefined;
17+
validator?(value: unknown): boolean;
1418
}
1519

1620
export type PropType<T> = PropConstructor<T> | PropConstructor<T>[];
@@ -30,30 +34,18 @@ type RequiredKeys<T, MakeDefaultRequired> = {
3034

3135
type OptionalKeys<T, MakeDefaultRequired> = Exclude<keyof T, RequiredKeys<T, MakeDefaultRequired>>;
3236

33-
type ExtractFunctionPropType<
34-
T extends Function,
35-
TArgs extends Array<any> = any[],
36-
TResult = any
37-
> = T extends (...args: TArgs) => TResult ? T : never;
38-
39-
type ExtractCorrectPropType<T> = T extends Function
40-
? ExtractFunctionPropType<T>
41-
: Exclude<T, Function>;
42-
4337
// prettier-ignore
4438
type InferPropType<T> = T extends null
4539
? any // null & true would fail to infer
46-
: T extends { type: null }
47-
? any // somehow `ObjectConstructor` when inferred from { (): T } becomes `any`
40+
: T extends { type: null | true }
41+
? any // As TS issue https://github.com/Microsoft/TypeScript/issues/14829 // somehow `ObjectConstructor` when inferred from { (): T } becomes `any` // `BooleanConstructor` when inferred from PropConstructor(with PropMethod) becomes `Boolean`
4842
: T extends ObjectConstructor | { type: ObjectConstructor }
4943
? { [key: string]: any }
50-
: T extends Prop<infer V, true | false>
51-
? ExtractCorrectPropType<V>
52-
: T;
53-
54-
// prettier-ignore
55-
export type ExtractPropTypes<O, MakeDefaultRequired extends boolean = true> = {
56-
readonly [K in RequiredKeys<O, MakeDefaultRequired>]: InferPropType<O[K]>;
57-
} & {
58-
readonly [K in OptionalKeys<O, MakeDefaultRequired>]?: InferPropType<O[K]>;
59-
};
44+
: T extends BooleanConstructor | { type: BooleanConstructor }
45+
? boolean
46+
: T extends Prop<infer V> ? V : T;
47+
48+
export type ExtractPropTypes<O, MakeDefaultRequired extends boolean = true> = O extends object
49+
? { [K in RequiredKeys<O, MakeDefaultRequired>]: InferPropType<O[K]> } &
50+
{ [K in OptionalKeys<O, MakeDefaultRequired>]?: InferPropType<O[K]> }
51+
: { [K in string]: any };

test/types/defineComponent.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,22 @@ describe('defineComponent', () => {
144144
expect.assertions(2);
145145
});
146146

147+
it('should accept tuple props', () => {
148+
const App = defineComponent({
149+
props: ['p1', 'p2'],
150+
setup(props) {
151+
props.p1;
152+
props.p2;
153+
type PropsType = typeof props;
154+
type Expected = { readonly p1?: any; readonly p2?: any };
155+
isSubType<Expected, PropsType>(true);
156+
isSubType<PropsType, Expected>(true);
157+
},
158+
});
159+
new Vue(App);
160+
expect.assertions(2);
161+
});
162+
147163
it('infer the required prop', () => {
148164
const App = defineComponent({
149165
props: {

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4034,10 +4034,10 @@ typedarray-to-buffer@^3.1.5:
40344034
dependencies:
40354035
is-typedarray "^1.0.0"
40364036

4037-
typescript@^3.9.3:
4038-
version "3.9.3"
4039-
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.3.tgz#d3ac8883a97c26139e42df5e93eeece33d610b8a"
4040-
integrity sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ==
4037+
typescript@^3.9.5:
4038+
version "3.9.5"
4039+
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36"
4040+
integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==
40414041

40424042
union-value@^1.0.0:
40434043
version "1.0.1"

0 commit comments

Comments
 (0)