Skip to content
This repository was archived by the owner on Sep 27, 2023. It is now read-only.

Commit 7e498f5

Browse files
authored
feat: add '| undefined' to optional input properties (#494)
Allow consumers to use the `exactOptionalPropertyTypes` flag by changing from ```ts foo?: Foo | null; ``` to ```ts foo?: Foo | null | undefined; ```
1 parent 2b797b4 commit 7e498f5

6 files changed

+329
-682
lines changed

src/TypeScriptGenerator.ts

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ import {
2424

2525
type Selection = {
2626
key: string;
27-
schemaName?: string;
28-
value?: any;
29-
nodeType?: TypeID;
30-
conditional?: boolean;
31-
concreteType?: string;
32-
ref?: string;
33-
nodeSelections?: SelectionMap | null;
34-
kind?: string;
35-
documentName?: string;
27+
schemaName?: string | undefined;
28+
value?: any | undefined;
29+
nodeType?: TypeID | undefined;
30+
conditional?: boolean | undefined;
31+
concreteType?: string | undefined;
32+
ref?: string | undefined;
33+
nodeSelections?: SelectionMap | null | undefined;
34+
kind?: string | undefined;
35+
documentName?: string | undefined;
3636
};
3737

3838
type SelectionMap = Map<string, Selection>;
@@ -309,10 +309,31 @@ function exactObjectTypeAnnotation(
309309

310310
const idRegex = /^[$a-zA-Z_][$a-z0-9A-Z_]*$/;
311311

312+
// union optional types with undefined for compat with exactOptionalPropertyTypes
313+
function createInexactOptionalType(type: ts.TypeNode): ts.TypeNode {
314+
if (ts.isUnionTypeNode(type)) {
315+
return ts.factory.updateUnionTypeNode(
316+
type,
317+
ts.factory.createNodeArray([
318+
...type.types,
319+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
320+
])
321+
);
322+
} else {
323+
return ts.factory.createUnionTypeNode([
324+
type,
325+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
326+
]);
327+
}
328+
}
329+
312330
function objectTypeProperty(
313331
propertyName: string,
314332
type: ts.TypeNode,
315-
options: { readonly?: boolean; optional?: boolean } = {}
333+
options: {
334+
readonly?: boolean | undefined;
335+
optional?: boolean | undefined;
336+
} = {}
316337
): ts.PropertySignature {
317338
const { optional, readonly = true } = options;
318339
const modifiers = readonly
@@ -325,7 +346,7 @@ function objectTypeProperty(
325346
? ts.factory.createIdentifier(propertyName)
326347
: ts.factory.createStringLiteral(propertyName),
327348
optional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined,
328-
type
349+
optional ? createInexactOptionalType(type) : type
329350
);
330351
}
331352

src/TypeScriptTypeTransformers.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export type ScalarTypeMapping = {
99
export type State = {
1010
generatedFragments: Set<string>;
1111
generatedInputObjectTypes: {
12-
[name: string]: ts.TypeNode | "pending";
12+
[name: string]: ts.TypeLiteralNode | "pending";
1313
};
1414
matchFields: Map<string, ts.TypeNode>;
1515
runtimeImports: Set<string>;
@@ -37,9 +37,7 @@ export function transformScalarType(
3737
} else {
3838
return ts.factory.createUnionTypeNode([
3939
transformNonNullableScalarType(schema, type, state, objectProps),
40-
ts.factory.createLiteralTypeNode(
41-
ts.factory.createToken(ts.SyntaxKind.NullKeyword)
42-
),
40+
ts.factory.createLiteralTypeNode(ts.factory.createNull()),
4341
]);
4442
}
4543
}
@@ -115,20 +113,29 @@ function transformGraphQLEnumType(
115113
export function transformInputType(
116114
schema: Schema,
117115
type: TypeID,
118-
state: State
116+
state: State,
117+
options: {
118+
inputObjectProperty?: boolean | undefined;
119+
} = {}
119120
): ts.TypeNode {
121+
const { inputObjectProperty } = options;
120122
if (schema.isNonNull(type)) {
121123
return transformNonNullableInputType(
122124
schema,
123125
schema.getNullableType(type),
124126
state
125127
);
128+
} else if (inputObjectProperty) {
129+
return ts.factory.createUnionTypeNode([
130+
transformNonNullableInputType(schema, type, state),
131+
ts.factory.createLiteralTypeNode(ts.factory.createNull()),
132+
// add undefined to support exactOptionalPropertyTypes
133+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
134+
]);
126135
} else {
127136
return ts.factory.createUnionTypeNode([
128137
transformNonNullableInputType(schema, type, state),
129-
ts.factory.createLiteralTypeNode(
130-
ts.factory.createToken(ts.SyntaxKind.NullKeyword)
131-
),
138+
ts.factory.createLiteralTypeNode(ts.factory.createNull()),
132139
]);
133140
}
134141
}
@@ -169,7 +176,9 @@ function transformNonNullableInputType(
169176
!schema.isNonNull(fieldType)
170177
? ts.factory.createToken(ts.SyntaxKind.QuestionToken)
171178
: undefined,
172-
transformInputType(schema, fieldType, state)
179+
transformInputType(schema, fieldType, state, {
180+
inputObjectProperty: true,
181+
})
173182
);
174183

175184
return property;

0 commit comments

Comments
 (0)