Skip to content

Commit ca98a50

Browse files
authored
Use const contexts and tuple mapped types to simplify some explicitly elucidated types (microsoft#30654)
* Use const contexts and tuple mapped types to simplify some explicitly elucidated types * Fix lint * Further cleanup - refactor mapped type into its own alias for readability * Slightly more cleanup - PragmaPsuedoMap neednt be partial, thus removing tons of nonull assertions * Remove GH#18217 comments
1 parent bf0dd79 commit ca98a50

File tree

2 files changed

+35
-44
lines changed

2 files changed

+35
-44
lines changed

src/compiler/parser.ts

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7749,17 +7749,17 @@ namespace ts {
77497749

77507750
context.pragmas = createMap() as PragmaMap;
77517751
for (const pragma of pragmas) {
7752-
if (context.pragmas.has(pragma!.name)) { // TODO: GH#18217
7753-
const currentValue = context.pragmas.get(pragma!.name);
7752+
if (context.pragmas.has(pragma.name)) {
7753+
const currentValue = context.pragmas.get(pragma.name);
77547754
if (currentValue instanceof Array) {
7755-
currentValue.push(pragma!.args);
7755+
currentValue.push(pragma.args);
77567756
}
77577757
else {
7758-
context.pragmas.set(pragma!.name, [currentValue, pragma!.args]);
7758+
context.pragmas.set(pragma.name, [currentValue, pragma.args]);
77597759
}
77607760
continue;
77617761
}
7762-
context.pragmas.set(pragma!.name, pragma!.args);
7762+
context.pragmas.set(pragma.name, pragma.args);
77637763
}
77647764
}
77657765

@@ -7783,9 +7783,8 @@ namespace ts {
77837783
const typeReferenceDirectives = context.typeReferenceDirectives;
77847784
const libReferenceDirectives = context.libReferenceDirectives;
77857785
forEach(toArray(entryOrList), (arg: PragmaPseudoMap["reference"]) => {
7786-
// TODO: GH#18217
7787-
const { types, lib, path } = arg!.arguments;
7788-
if (arg!.arguments["no-default-lib"]) {
7786+
const { types, lib, path } = arg.arguments;
7787+
if (arg.arguments["no-default-lib"]) {
77897788
context.hasNoDefaultLib = true;
77907789
}
77917790
else if (types) {
@@ -7798,41 +7797,41 @@ namespace ts {
77987797
referencedFiles.push({ pos: path.pos, end: path.end, fileName: path.value });
77997798
}
78007799
else {
7801-
reportDiagnostic(arg!.range.pos, arg!.range.end - arg!.range.pos, Diagnostics.Invalid_reference_directive_syntax);
7800+
reportDiagnostic(arg.range.pos, arg.range.end - arg.range.pos, Diagnostics.Invalid_reference_directive_syntax);
78027801
}
78037802
});
78047803
break;
78057804
}
78067805
case "amd-dependency": {
78077806
context.amdDependencies = map(
78087807
toArray(entryOrList),
7809-
(x: PragmaPseudoMap["amd-dependency"]) => ({ name: x!.arguments.name!, path: x!.arguments.path })); // TODO: GH#18217
7808+
(x: PragmaPseudoMap["amd-dependency"]) => ({ name: x.arguments.name, path: x.arguments.path }));
78107809
break;
78117810
}
78127811
case "amd-module": {
78137812
if (entryOrList instanceof Array) {
78147813
for (const entry of entryOrList) {
78157814
if (context.moduleName) {
78167815
// TODO: It's probably fine to issue this diagnostic on all instances of the pragma
7817-
reportDiagnostic(entry!.range.pos, entry!.range.end - entry!.range.pos, Diagnostics.An_AMD_module_cannot_have_multiple_name_assignments);
7816+
reportDiagnostic(entry.range.pos, entry.range.end - entry.range.pos, Diagnostics.An_AMD_module_cannot_have_multiple_name_assignments);
78187817
}
7819-
context.moduleName = (entry as PragmaPseudoMap["amd-module"])!.arguments.name;
7818+
context.moduleName = (entry as PragmaPseudoMap["amd-module"]).arguments.name;
78207819
}
78217820
}
78227821
else {
7823-
context.moduleName = (entryOrList as PragmaPseudoMap["amd-module"])!.arguments.name;
7822+
context.moduleName = (entryOrList as PragmaPseudoMap["amd-module"]).arguments.name;
78247823
}
78257824
break;
78267825
}
78277826
case "ts-nocheck":
78287827
case "ts-check": {
78297828
// _last_ of either nocheck or check in a file is the "winner"
78307829
forEach(toArray(entryOrList), entry => {
7831-
if (!context.checkJsDirective || entry!.range.pos > context.checkJsDirective.pos) { // TODO: GH#18217
7830+
if (!context.checkJsDirective || entry.range.pos > context.checkJsDirective.pos) {
78327831
context.checkJsDirective = {
78337832
enabled: key === "ts-check",
7834-
end: entry!.range.end,
7835-
pos: entry!.range.pos
7833+
end: entry.range.end,
7834+
pos: entry.range.pos
78367835
};
78377836
}
78387837
});

src/compiler/types.ts

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5950,26 +5950,18 @@ namespace ts {
59505950
/* @internal */
59515951
export interface PragmaDefinition<T1 extends string = string, T2 extends string = string, T3 extends string = string, T4 extends string = string> {
59525952
args?:
5953-
| [PragmaArgumentSpecification<T1>]
5954-
| [PragmaArgumentSpecification<T1>, PragmaArgumentSpecification<T2>]
5955-
| [PragmaArgumentSpecification<T1>, PragmaArgumentSpecification<T2>, PragmaArgumentSpecification<T3>]
5956-
| [PragmaArgumentSpecification<T1>, PragmaArgumentSpecification<T2>, PragmaArgumentSpecification<T3>, PragmaArgumentSpecification<T4>];
5953+
| readonly [PragmaArgumentSpecification<T1>]
5954+
| readonly [PragmaArgumentSpecification<T1>, PragmaArgumentSpecification<T2>]
5955+
| readonly [PragmaArgumentSpecification<T1>, PragmaArgumentSpecification<T2>, PragmaArgumentSpecification<T3>]
5956+
| readonly [PragmaArgumentSpecification<T1>, PragmaArgumentSpecification<T2>, PragmaArgumentSpecification<T3>, PragmaArgumentSpecification<T4>];
59575957
// If not present, defaults to PragmaKindFlags.Default
59585958
kind?: PragmaKindFlags;
59595959
}
59605960

5961-
/**
5962-
* This function only exists to cause exact types to be inferred for all the literals within `commentPragmas`
5963-
*/
5964-
/* @internal */
5965-
function _contextuallyTypePragmas<T extends {[name: string]: PragmaDefinition<K1, K2, K3, K4>}, K1 extends string, K2 extends string, K3 extends string, K4 extends string>(args: T): T {
5966-
return args;
5967-
}
5968-
59695961
// While not strictly a type, this is here because `PragmaMap` needs to be here to be used with `SourceFile`, and we don't
59705962
// fancy effectively defining it twice, once in value-space and once in type-space
59715963
/* @internal */
5972-
export const commentPragmas = _contextuallyTypePragmas({
5964+
export const commentPragmas = {
59735965
"reference": {
59745966
args: [
59755967
{ name: "types", optional: true, captureSpan: true },
@@ -5997,7 +5989,7 @@ namespace ts {
59975989
args: [{ name: "factory" }],
59985990
kind: PragmaKindFlags.MultiLine
59995991
},
6000-
});
5992+
} as const;
60015993

60025994
/* @internal */
60035995
type PragmaArgTypeMaybeCapture<TDesc> = TDesc extends {captureSpan: true} ? {value: string, pos: number, end: number} : string;
@@ -6008,29 +6000,29 @@ namespace ts {
60086000
? {[K in TName]?: PragmaArgTypeMaybeCapture<TDesc>}
60096001
: {[K in TName]: PragmaArgTypeMaybeCapture<TDesc>};
60106002

6003+
/* @internal */
6004+
type UnionToIntersection<U> =
6005+
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
6006+
6007+
/* @internal */
6008+
type ArgumentDefinitionToFieldUnion<T extends readonly PragmaArgumentSpecification<any>[]> = {
6009+
[K in keyof T]: PragmaArgTypeOptional<T[K], T[K] extends {name: infer TName} ? TName extends string ? TName : never : never>
6010+
}[Extract<keyof T, number>]; // The mapped type maps over only the tuple members, but this reindex gets _all_ members - by extracting only `number` keys, we get only the tuple members
6011+
60116012
/**
60126013
* Maps a pragma definition into the desired shape for its arguments object
6013-
* Maybe the below is a good argument for types being iterable on struture in some way.
60146014
*/
60156015
/* @internal */
6016-
type PragmaArgumentType<T extends PragmaDefinition> =
6017-
T extends { args: [PragmaArgumentSpecification<infer TName1>, PragmaArgumentSpecification<infer TName2>, PragmaArgumentSpecification<infer TName3>, PragmaArgumentSpecification<infer TName4>] }
6018-
? PragmaArgTypeOptional<T["args"][0], TName1> & PragmaArgTypeOptional<T["args"][1], TName2> & PragmaArgTypeOptional<T["args"][2], TName3> & PragmaArgTypeOptional<T["args"][2], TName4>
6019-
: T extends { args: [PragmaArgumentSpecification<infer TName1>, PragmaArgumentSpecification<infer TName2>, PragmaArgumentSpecification<infer TName3>] }
6020-
? PragmaArgTypeOptional<T["args"][0], TName1> & PragmaArgTypeOptional<T["args"][1], TName2> & PragmaArgTypeOptional<T["args"][2], TName3>
6021-
: T extends { args: [PragmaArgumentSpecification<infer TName1>, PragmaArgumentSpecification<infer TName2>] }
6022-
? PragmaArgTypeOptional<T["args"][0], TName1> & PragmaArgTypeOptional<T["args"][1], TName2>
6023-
: T extends { args: [PragmaArgumentSpecification<infer TName>] }
6024-
? PragmaArgTypeOptional<T["args"][0], TName>
6025-
: object;
6026-
// The above fallback to `object` when there's no args to allow `{}` (as intended), but not the number 2, for example
6027-
// TODO: Swap to `undefined` for a cleaner API once strictNullChecks is enabled
6016+
type PragmaArgumentType<KPrag extends keyof ConcretePragmaSpecs> =
6017+
ConcretePragmaSpecs[KPrag] extends { args: readonly PragmaArgumentSpecification<any>[] }
6018+
? UnionToIntersection<ArgumentDefinitionToFieldUnion<ConcretePragmaSpecs[KPrag]["args"]>>
6019+
: never;
60286020

60296021
/* @internal */
60306022
type ConcretePragmaSpecs = typeof commentPragmas;
60316023

60326024
/* @internal */
6033-
export type PragmaPseudoMap = {[K in keyof ConcretePragmaSpecs]?: {arguments: PragmaArgumentType<ConcretePragmaSpecs[K]>, range: CommentRange}};
6025+
export type PragmaPseudoMap = {[K in keyof ConcretePragmaSpecs]: {arguments: PragmaArgumentType<K>, range: CommentRange}};
60346026

60356027
/* @internal */
60366028
export type PragmaPseudoMapEntry = {[K in keyof PragmaPseudoMap]: {name: K, args: PragmaPseudoMap[K]}}[keyof PragmaPseudoMap];

0 commit comments

Comments
 (0)