Skip to content

Enable noUncheckedIndexedAccess #3867

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions resources/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,9 @@ function runBenchmark(
benchmark: string,
benchmarkProjects: ReadonlyArray<BenchmarkProject>,
) {
const results = [];
for (let i = 0; i < benchmarkProjects.length; ++i) {
const { revision, projectPath } = benchmarkProjects[i];
const results: Array<BenchmarkComputedStats> = [];

benchmarkProjects.forEach(({ revision, projectPath }, i) => {
const modulePath = path.join(projectPath, benchmark);

if (i === 0) {
Expand All @@ -288,7 +288,8 @@ function runBenchmark(
} catch (error) {
console.log(' ' + revision + ': ' + red(error.message));
}
}
});

console.log('\n');

beautifyBenchmark(results);
Expand Down
1 change: 1 addition & 0 deletions resources/build-npm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ function buildPackage(outDir: string, isESMOnly: boolean): void {
const splittedTag = preReleaseTag.split('.');
// Note: `experimental-*` take precedence over `alpha`, `beta` or `rc`.
const versionTag = splittedTag[2] ?? splittedTag[0];
assert(versionTag);
assert(
['alpha', 'beta', 'rc'].includes(versionTag) ||
versionTag.startsWith('experimental-'),
Expand Down
3 changes: 3 additions & 0 deletions resources/diff-npm-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ if (args.length < 2) {
);
}

assert(fromRevision);
assert(toRevision);

console.log(`📦 Building NPM package for ${fromRevision}...`);
const fromPackage = prepareNPMPackage(fromRevision);

Expand Down
14 changes: 11 additions & 3 deletions resources/gen-changelog.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import assert from 'node:assert';

import { git, readPackageJSON } from './utils.js';

const packageJSON = readPackageJSON();
Expand Down Expand Up @@ -88,11 +90,13 @@ async function genChangeLog(): Promise<string> {
}

const label = labels[0];
assert(label);
if (!labelsConfig[label]) {
throw new Error(`Unknown label: ${label}. See ${pr.url}`);
}
byLabel[label] ??= [];
byLabel[label].push(pr);
const prByLabel = byLabel[label] ?? [];
byLabel[label] = prByLabel;
prByLabel.push(pr);
committersByLogin[pr.author.login] = pr.author;
}

Expand Down Expand Up @@ -285,7 +289,11 @@ function commitInfoToPR(commit: CommitInfo): number {
);
}

return associatedPRs[0].number;
const pr = associatedPRs[0];

assert(pr);

return pr.number;
}

async function getPRsInfo(
Expand Down
4 changes: 4 additions & 0 deletions resources/inline-invariant.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import assert from 'node:assert';

import ts from 'typescript';

/**
Expand Down Expand Up @@ -29,6 +31,8 @@ export function inlineInvariant(context: ts.TransformationContext) {
if (funcName === 'invariant' || funcName === 'devAssert') {
const [condition, ...otherArgs] = args;

assert(condition);

return factory.createBinaryExpression(
factory.createParenthesizedExpression(condition),
ts.SyntaxKind.BarBarToken,
Expand Down
10 changes: 6 additions & 4 deletions resources/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,12 @@ export function showDirStats(dirPath: string): void {
const ext = name.split('.').slice(1).join('.');
const filetype = ext ? '*.' + ext : name;

fileTypes[filetype] ??= { filepaths: [], size: 0 };
const dirStats = fileTypes[filetype] ?? { filepaths: [], size: 0 };
fileTypes[filetype] = dirStats;

totalSize += stats.size;
fileTypes[filetype].size += stats.size;
fileTypes[filetype].filepaths.push(filepath);
dirStats.size += stats.size;
dirStats.filepaths.push(filepath);
}

const stats: Array<[string, number]> = [];
Expand All @@ -163,13 +164,14 @@ export function showDirStats(dirPath: string): void {
if (numFiles > 1) {
stats.push([filetype + ' x' + numFiles, typeStats.size]);
} else {
assert(typeStats.filepaths[0]);
const relativePath = path.relative(dirPath, typeStats.filepaths[0]);
stats.push([relativePath, typeStats.size]);
}
}
stats.sort((a, b) => b[1] - a[1]);

const prettyStats = stats.map(([type, size]) => [
const prettyStats = stats.map<[string, string]>(([type, size]) => [
type,
(size / 1024).toFixed(2) + ' KB',
]);
Expand Down
4 changes: 2 additions & 2 deletions src/__testUtils__/dedent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ export function dedent(
strings: ReadonlyArray<string>,
...values: ReadonlyArray<string>
): string {
let str = strings[0];
let str = `${strings[0]}`;

for (let i = 1; i < strings.length; ++i) {
str += values[i - 1] + strings[i]; // interpolation
str += `${values[i - 1]}${strings[i]}`; // interpolation
}
return dedentString(str);
}
8 changes: 8 additions & 0 deletions src/__testUtils__/expectMatchingValues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@ import { expectJSON } from './expectJSON.js';

export function expectMatchingValues<T>(values: ReadonlyArray<T>): T {
const [firstValue, ...remainingValues] = values;

/* c8 ignore start */
if (firstValue === undefined) {
throw new Error('Expected a non-empty array');
}
/* c8 ignore stop */

for (const value of remainingValues) {
expectJSON(value).toDeepEqual(firstValue);
}

return firstValue;
}
71 changes: 44 additions & 27 deletions src/__tests__/starWarsData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export interface Character {
appearsIn: ReadonlyArray<number>;
}

export interface Human {
export interface Human extends Character {
type: 'Human';
id: string;
name: string;
Expand All @@ -18,7 +18,7 @@ export interface Human {
homePlanet?: string;
}

export interface Droid {
export interface Droid extends Character {
type: 'Droid';
id: string;
name: string;
Expand All @@ -35,93 +35,110 @@ export interface Droid {
* JSON objects in a more complex demo.
*/

const luke: Human = {
const luke = {
type: 'Human',
id: '1000',
name: 'Luke Skywalker',
friends: ['1002', '1003', '2000', '2001'],
appearsIn: [4, 5, 6],
homePlanet: 'Tatooine',
};
} as const satisfies Human;

const vader: Human = {
const vader = {
type: 'Human',
id: '1001',
name: 'Darth Vader',
friends: ['1004'],
appearsIn: [4, 5, 6],
homePlanet: 'Tatooine',
};
} as const satisfies Human;

const han: Human = {
const han = {
type: 'Human',
id: '1002',
name: 'Han Solo',
friends: ['1000', '1003', '2001'],
appearsIn: [4, 5, 6],
};
} as const satisfies Human;

const leia: Human = {
const leia = {
type: 'Human',
id: '1003',
name: 'Leia Organa',
friends: ['1000', '1002', '2000', '2001'],
appearsIn: [4, 5, 6],
homePlanet: 'Alderaan',
};
} as const satisfies Human;

const tarkin: Human = {
const tarkin = {
type: 'Human',
id: '1004',
name: 'Wilhuff Tarkin',
friends: ['1001'],
appearsIn: [4],
};
} as const satisfies Human;

const humanData: { [id: string]: Human } = {
const humanData = {
[luke.id]: luke,
[vader.id]: vader,
[han.id]: han,
[leia.id]: leia,
[tarkin.id]: tarkin,
};
} as const satisfies { [id: string]: Human };

const threepio: Droid = {
type HumanData = typeof humanData;
type AnyHumanId = keyof HumanData;
type AnyHuman = HumanData[AnyHumanId];

const threepio = {
type: 'Droid',
id: '2000',
name: 'C-3PO',
friends: ['1000', '1002', '1003', '2001'],
appearsIn: [4, 5, 6],
primaryFunction: 'Protocol',
};
} as const satisfies Droid;

const artoo: Droid = {
const artoo = {
type: 'Droid',
id: '2001',
name: 'R2-D2',
friends: ['1000', '1002', '1003'],
appearsIn: [4, 5, 6],
primaryFunction: 'Astromech',
};
} as const satisfies Droid;

const droidData: { [id: string]: Droid } = {
const droidData = {
[threepio.id]: threepio,
[artoo.id]: artoo,
};
} as const satisfies { [id: string]: Droid };

type DroidData = typeof droidData;
type AnyDroidId = keyof DroidData;
type AnyDroid = DroidData[AnyDroidId];

type AnyCharacter = AnyHuman | AnyDroid;
type AnyCharacterId = AnyCharacter['id'];

const isHumanId = (id: AnyCharacterId): id is AnyHumanId =>
Object.hasOwn(humanData, id);

/**
* Helper function to get a character by ID.
*/
function getCharacter(id: string): Promise<Character | null> {
// Returning a promise just to illustrate that GraphQL.js supports it.
return Promise.resolve(humanData[id] ?? droidData[id]);
function getCharacter(id: AnyCharacterId): Promise<AnyCharacter> {
if (isHumanId(id)) {
return Promise.resolve(humanData[id]);
}

return Promise.resolve(droidData[id]);
}

/**
* Allows us to query for a character's friends.
*/
export function getFriends(
character: Character,
export function getFriends<T extends AnyCharacter>(
character: T,
): Array<Promise<Character | null>> {
// Notice that GraphQL accepts Arrays of Promises.
return character.friends.map((id) => getCharacter(id));
Expand All @@ -142,13 +159,13 @@ export function getHero(episode: number): Character {
/**
* Allows us to query for the human with the given id.
*/
export function getHuman(id: string): Human | null {
export function getHuman(id: AnyHumanId): AnyHuman {
return humanData[id];
}

/**
* Allows us to query for the droid with the given id.
*/
export function getDroid(id: string): Droid | null {
export function getDroid(id: AnyDroidId): Droid {
return droidData[id];
}
8 changes: 5 additions & 3 deletions src/error/__tests__/GraphQLError-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const source = new Source(dedent`
`);
const ast = parse(source);
const operationNode = ast.definitions[0];
assert(operationNode.kind === Kind.OPERATION_DEFINITION);
assert(operationNode?.kind === Kind.OPERATION_DEFINITION);
const fieldNode = operationNode.selectionSet.selections[0];
assert(fieldNode != null);

Expand Down Expand Up @@ -247,8 +247,9 @@ describe('toString', () => {
),
);
const opA = docA.definitions[0];
assert(opA.kind === Kind.OBJECT_TYPE_DEFINITION && opA.fields != null);
assert(opA?.kind === Kind.OBJECT_TYPE_DEFINITION && opA.fields != null);
const fieldA = opA.fields[0];
assert(fieldA);

const docB = parse(
new Source(
Expand All @@ -261,8 +262,9 @@ describe('toString', () => {
),
);
const opB = docB.definitions[0];
assert(opB.kind === Kind.OBJECT_TYPE_DEFINITION && opB.fields != null);
assert(opB?.kind === Kind.OBJECT_TYPE_DEFINITION && opB.fields != null);
const fieldB = opB.fields[0];
assert(fieldB);

const error = new GraphQLError('Example error with two nodes', {
nodes: [fieldA.type, fieldB.type],
Expand Down
2 changes: 1 addition & 1 deletion src/execution/__tests__/executor-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ describe('Execute: Handles basic execution tasks', () => {
);

const operation = document.definitions[0];
assert(operation.kind === Kind.OPERATION_DEFINITION);
assert(operation?.kind === Kind.OPERATION_DEFINITION);

expect(resolvedInfo).to.include({
fieldName: 'test',
Expand Down
Loading