-
Notifications
You must be signed in to change notification settings - Fork 108
Migrate to TypeScript 5.9 #1799
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
base: master
Are you sure you want to change the base?
Conversation
Pull Request Test Coverage Report for Build 17011975380Details
💛 - Coveralls |
Is the stepper officially deprecated (and replaced with the tracer)? |
Is it not just a naming thing? I'm unaware of the developments; this PR tries to be as small as possible and not change any behavior (just trying to update TS) I do have a subsequent PR that enables |
I ask because I briefly toyed with a similar migration and got tired of fixing the stepper but then I checked and nothing refers to the stepper outside of the stepper folder. |
Prof @martin-henz is the stepper gone? @leeyi45 Either way, I think we can just remove it (if we have to) in a future PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general LGTM, not sure if we want to pursue the path of fewer type assertions and more usage of typeguards and stuff
@@ -260,8 +260,8 @@ function transformSomeExpressionsToCheckIfBoolean(program: es.Program, globalIds | |||
const { line, column } = (node.loc ?? UNKNOWN_LOCATION).start | |||
const source = node.loc?.source ?? null | |||
const test = node.type === 'LogicalExpression' ? 'left' : 'test' | |||
node[test] = create.callExpression(globalIds.boolOrErr, [ | |||
node[test], | |||
;(node as any)[test] = create.callExpression(globalIds.boolOrErr, [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather have this written as
const testNode = node.type === 'LogicalExpression' ? node.left : node.test
const callExpr = create.callExpression(... whatever)
if (node.type === 'LogicalExpression') {
node.left = callExpr
} else {
node.test = callExpr
}
Then we can avoid the type assertion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^^ we should do this, we should aim to reduce the amount of total type assertions/typeguards
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, originally I didn't want this PR to introduce behavioural changes (and leave these cleanup tasks to the future instead) but on second thought it makes sense to do it as part of this PR (since the goal of migrating is for type safety anyway)
Although, having to have these awkward semicolon placements due to the fact the codebase doesn't use semicolons by default bugs me. It may work for plain JS but TS is not the place for no semicolons imo.
What do you think of adding semicolons into the codebase?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we expanding the scope of the PR to include general type safety? I can include some changes I was working on for the cse machine
@@ -23,7 +22,7 @@ export function chapterParser(str: string): Chapter { | |||
} | |||
|
|||
if (foundChapter in Chapter) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For these, I wrote a typeguard:
export function isKeyOf<T extends Record<string | number | symbol, unknown>>(obj: T, p: string | number | symbol): p is keyof T {
return p in obj;
}
Then you can just use it like
if (isKeyOf(str, Chapter)) {
return Chapter[str]
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^ stylistically I agree and would rather use a typeguard to avoid explicit type assertions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, thanks, will make the changes
src/stepper/converter.ts
Outdated
@@ -62,7 +62,7 @@ export function nodeToValueWithContext(node: substituterNodes, context: Context) | |||
return node.type === 'Literal' | |||
? node.value | |||
: util.isBuiltinFunction(node) | |||
? builtin[(node as es.Identifier).name] | |||
? builtin[(node as es.Identifier).name as keyof typeof builtin] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we change isBuiltInFunction
to a proper type guard we won't need the type assertion:
type BuiltinIdentifier = Omit<es.Identifier, 'name'> & {
name: keyof typeof builtin
}
export function isBuiltinFunction(node: SubstituterNodes): node is BuiltinIdentifier {
if (!isIdentifier(node) || !isKeyType(builtin, node.name)) return false
// ...the other checks
}
The tracer is the new stepper, and should completely replace it. I'm double checking with the Link: #1811 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with a few nits. I am a bit concerned with the amount of additional type gymnastics we have to do, but that's more to do with the codebase quality than this PR. Additionally, I agree with @leeyi45 and would argue for typeguards > over raw type assertions, but this is more of a stylictic choice.
Should we draft out a style guide for Source Academy (and maybe recommend to write code in ways that avoid the need for playing with the typechecker?)
@@ -138,7 +138,9 @@ export class State { | |||
} | |||
const transitions = this.mixedStack[this.stackPointer].transitions | |||
for (let i = 0; i < transitions.length; i++) { | |||
const transition = transitions[i] | |||
// TODO: Something seems to be very wrong with the type definitions here | |||
// Shall we just remove the infinite loop detector as per #1516? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we reraise this as an issue? I know that in #1516 it was agreed that the component was useful, but with the problems we are having with it (increased complexity, here we see strange issues with types) we may want to reimplement it (probably only viable as a student project if it's breaking?) or reevaluate it again
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Normally, I try to fix type issues as I go (even if it means weird typecasts etc.) but this is beyond me; either it's some super old TS logic that predates my knowledge of the "modern" 4.x and 5.x versions or it's just (ab)use of TS that was somehow never caught.
Without looking at the code and figuring out how this all works I had to resort to just putting an any
there.
I suspect the original type definitions may have not been entirely correct, but since TS used to implicitly give an any
for these kinds of invalid accesses (and they are accessed in a way consistent and expected of the actual contract/behaviour) then it all just magically seems to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with removing the feature for now. Re-implementing it will lead to a cleaner implementation.
The project is well-documented in a report and can be revived from there.
I don't think the system will be missed very much. It carries a bit of infrastructure with a server side, sentry support etc that was needed for carrying out the project.
So yea, please go ahead and remove it.
@@ -28,7 +28,8 @@ export function __py_adder(x: Value, y: Value) { | |||
return x + Number(y) | |||
} | |||
if (__is_numeric(x) && __is_numeric(y)) { | |||
return x + y | |||
// TODO: Shouldn't this be `Number(x) + Number(y)`? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Fidget-Spinner ^ thoughts? 😅
@@ -260,8 +260,8 @@ function transformSomeExpressionsToCheckIfBoolean(program: es.Program, globalIds | |||
const { line, column } = (node.loc ?? UNKNOWN_LOCATION).start | |||
const source = node.loc?.source ?? null | |||
const test = node.type === 'LogicalExpression' ? 'left' : 'test' | |||
node[test] = create.callExpression(globalIds.boolOrErr, [ | |||
node[test], | |||
;(node as any)[test] = create.callExpression(globalIds.boolOrErr, [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^^ we should do this, we should aim to reduce the amount of total type assertions/typeguards
@@ -23,7 +22,7 @@ export function chapterParser(str: string): Chapter { | |||
} | |||
|
|||
if (foundChapter in Chapter) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^ stylistically I agree and would rather use a typeguard to avoid explicit type assertions
I think a big part of the reason is that a lot of Typescript tricks wouldn't be immediately obvious to someone that hasn't been working with the language for that long so things like Typeguards won't come to mind first. Like I was staring at the code for the cse machine and there's a ton of type assertions that don't need to be there |
Description
Supersedes #1666.
Type of change
How to test
Checklist