Skip to content
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

ci: add TypeScript type check #989

Merged
merged 15 commits into from
Dec 19, 2024
27 changes: 27 additions & 0 deletions .github/workflows/typescript-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Check TypeScript types

on:
push:
branches:
- main
- 'release/**'
- 'feature/**'
pull_request:
branches:
- main
- 'release/**'
- 'feature/**'

jobs:
typescript-check:
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Check TypeScript types
run: python tools/check_typescript_ci.py

Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function DashboardPlugin({
widget: VariableDefinition;
}) => {
const { id: widgetId, name, type } = widget;
if (type === dh.VariableType.TABLE || type === dh.VariableType.FIGURE) {
if (type === 'Table' || type === 'Figure') {
// Just ignore table and figure types - only want interesting other types
return;
}
Expand Down
3 changes: 2 additions & 1 deletion plugins/ui/src/js/src/elements/SearchField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import {
useKeyboardEventCallback,
} from './hooks';
import useDebouncedOnChange from './hooks/useDebouncedOnChange';
import { SerializedTextInputEventProps } from './model';

export function SearchField(
props: DHCSearchFieldProps & {
props: SerializedTextInputEventProps<DHCSearchFieldProps, string> & {
onSubmit?: (value: string) => void;
onClear?: () => void;
}
Expand Down
4 changes: 3 additions & 1 deletion plugins/ui/src/js/src/elements/UITable/UITable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,9 @@ export function UITable({
if (alwaysFetchColumnsArray[0] === false) {
return [];
}
return alwaysFetchColumnsArray.filter(v => typeof v === 'string');
return alwaysFetchColumnsArray.filter(
v => typeof v === 'string'
) as string[];
}, [alwaysFetchColumnsArray, modelColumns]);

const mouseHandlers = useMemo(
Expand Down
12 changes: 12 additions & 0 deletions plugins/ui/src/js/src/elements/UITable/UITableModel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ describe('Formatting', () => {
table: MOCK_TABLE,
databars: [],
format: [{ color: 'red' }, { color: 'blue' }],
displayNameMap: {},
});
expect(model.getFormatOptionForCell(0, 0, 'color')).toBe('blue');
expect(model.getFormatOptionForCell(1, 1, 'color')).toBe('blue');
Expand All @@ -48,6 +49,7 @@ describe('Formatting', () => {
{ cols: 'column0', color: 'red' },
{ cols: 'column1', color: 'blue' },
],
displayNameMap: {},
});
expect(model.getFormatOptionForCell(0, 0, 'color')).toBe('red');
expect(model.getFormatOptionForCell(1, 1, 'color')).toBe('blue');
Expand All @@ -71,6 +73,7 @@ describe('Formatting', () => {
{ color: 'red', if_: 'even' },
{ cols: 'column1', color: 'blue', if_: 'even' },
],
displayNameMap: {},
});
expect(model.getFormatOptionForCell(0, 0, 'color')).toBe('red');
expect(model.getFormatOptionForCell(0, 1, 'color')).toBeUndefined();
Expand All @@ -86,6 +89,7 @@ describe('Formatting', () => {
table: MOCK_TABLE,
databars: [],
format: [{ cols: 'column0', color: 'red' }],
displayNameMap: {},
});
expect(model.getFormatOptionForCell(1, 1, 'color')).toBeUndefined();
expect(
Expand All @@ -104,6 +108,7 @@ describe('Formatting', () => {
table: MOCK_TABLE,
databars: [],
format: [{ color: 'red', if_: 'even' }],
displayNameMap: {},
});
expect(model.getFormatOptionForCell(0, 0, 'color')).toBeUndefined();
expect(model.getFormatOptionForCell(0, 1, 'color')).toBeUndefined();
Expand All @@ -119,6 +124,7 @@ describe('Formatting', () => {
table: MOCK_TABLE,
databars: [],
format: [{ color: 'red' }],
displayNameMap: {},
});
expect(model.colorForCell(0, 0, {} as IrisGridThemeType)).toBe('red');
});
Expand All @@ -130,6 +136,7 @@ describe('Formatting', () => {
table: MOCK_TABLE,
databars: [],
format: [],
displayNameMap: {},
});
expect(model.colorForCell(0, 0, {} as IrisGridThemeType)).toBeUndefined();
expect(MOCK_BASE_MODEL.colorForCell).toHaveBeenCalledTimes(1);
Expand All @@ -145,6 +152,7 @@ describe('Formatting', () => {
table: MOCK_TABLE,
databars: [],
format: [{ background_color: 'black' }],
displayNameMap: {},
});
expect(
model.colorForCell(0, 0, { white: 'white' } as IrisGridThemeType)
Expand All @@ -162,6 +170,7 @@ describe('Formatting', () => {
table: MOCK_TABLE,
databars: [],
format: [{ background_color: 'white' }],
displayNameMap: {},
});
expect(
model.colorForCell(0, 0, { black: 'black' } as IrisGridThemeType)
Expand All @@ -176,6 +185,7 @@ describe('Formatting', () => {
table: MOCK_TABLE,
databars: [],
format: [{ color: 'foo' }],
displayNameMap: {},
});
model.setColorMap(new Map([['foo', 'bar']]));
expect(model.colorForCell(0, 0, {} as IrisGridThemeType)).toBe('bar');
Expand All @@ -190,6 +200,7 @@ describe('Formatting', () => {
table: MOCK_TABLE,
databars: [],
format: [],
displayNameMap: {},
});
expect(
model.backgroundColorForCell(0, 0, {} as IrisGridThemeType)
Expand All @@ -204,6 +215,7 @@ describe('Formatting', () => {
table: MOCK_TABLE,
databars: [],
format: [{ background_color: 'foo' }],
displayNameMap: {},
});
model.setColorMap(new Map([['foo', 'bar']]));
expect(model.backgroundColorForCell(0, 0, {} as IrisGridThemeType)).toBe(
Expand Down
50 changes: 50 additions & 0 deletions tools/check_typescript_ci.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from re import fullmatch
from subprocess import run
from sys import exit


def main():
print("Checking TypeScript types...")
res = run(
["npx", "tsc", "-p", ".", "--emitDeclarationOnly", "false", "--noEmit"],
capture_output=True,
)

if res.returncode == 0:
return 0

messages = []
for line in res.stdout.decode("utf-8").splitlines():
if len(line) == 0:
continue
# If there's an indent, that means it's a continuation of the previous line
# For example, the error message could be like:
# > Argument of type 'FUNCTION_1_TYPE | undefined' is not assignable to parameter of type 'FUNCTION_2_TYPE'.
# > Type 'FUNCTION_1_TYPE' is not assignable to type 'FUNCTION_2_TYPE'.
# > Types of parameters `PARAM_1` and `PARAM_2` are incompatible.
# > Type 'PARAM_1_TYPE' is not assignable to type 'PARAM_2_TYPE'.
if line[0] == " " and len(messages) > 0:
messages[-1] += "\n" + line
else:
messages.append(line)

for message in messages:
# Check if the message is actually an error and extract the details
# Error message format: file(line,col): error_message
match = fullmatch(r"(.+?)\((\d+),(\d+)\): ([\s\S]+)", message)
if match is None:
continue

file, line, col, error_message = match.groups()
# Newlines in GitHub Actions annotations are escaped as %0A
# https://github.com/actions/toolkit/issues/193#issuecomment-605394935
error_message = error_message.replace("\n", "%0A")
# GitHub Actions annotation format
print(f"::error file={file},line={line},col={col}::{error_message}")

return res.returncode


if __name__ == "__main__":
# Exit with returncode so GitHub Actions fails properly
exit(main())
Loading