fix(packages/codegen): fix debug assert fail printing TSJSDocNonNullableType (#27373)

When printing `<` followed by `!` in a TS type e.g. `Array<!T>`, the `last` category recorded is `CAT_OP_UN_NOT` instead of `CAT_OP_LT_THEN_UN_NOT`, which is the more usual category to record for `<!`.

In this case, `CAT_OP_UN_NOT` is fine. The purpose of `CAT_OP_LT_THEN_UN_NOT` is to insert a space before printing `--`, which would form an HTML-style comment `<!--`. In this case, that's not possible - no type can begin with `--` - so this hazard doesn't exist.

The problem was the checks in debug builds which make sure that `<!` always results in `CAT_OP_LT_THEN_UN_NOT`. Before this PR, printing `Array<!T>` would throw in tests / conformance.

Fix this by writing `CAT_OP_LT_THEN_UN_NOT` in debug builds. In release builds, continue to write `CAT_OP_UN_NOT` as it's cheaper, and the debug checks don't run in release builds.
This commit is contained in:
overlookmotel committed 2026-10-06 11:35:03 +00:00
1 parent ccb1c0f67a
commit bebef8836b
2 files changed
+15 -1

No files matched your search

+10 -1
View File
@@ -5,6 +5,7 @@ import {
CAT_CLOSE_BRACKET,
CAT_IDENT,
CAT_LT,
CAT_OP_LT_THEN_UN_NOT,
CAT_OP_UN_NOT,
CAT_OTHER,
CAT_QUESTION,
@@ -266,7 +267,15 @@ function printTSType(
printTSType(tsTypeAnnotationOf(node.typeAnnotation), state);
write(state, "!", CAT_OP_UN_NOT);
} else {
write(state, "!", CAT_OP_UN_NOT);
// `CAT_OP_LT_THEN_UN_NOT` exists to keep a `--` written next from completing `<!--`.
// That can't happen here - a type follows the `!`, and no type starts with `--`.
// So `CAT_OP_UN_NOT` is always safe here.
// In debug builds we have to use `CAT_OP_LT_THEN_UN_NOT` after a `<` (e.g. `Array<!string>`),
// because `debugAssertCategoryMatches` requires it for every `!` which follows a `<`,
// and would throw in tests if it's not.
// In release builds, we use `CAT_OP_UN_NOT` unconditionally, to avoid the branch.
debugAssertLastFresh(state);
write(state, "!", DEBUG && state.last === CAT_LT ? CAT_OP_LT_THEN_UN_NOT : CAT_OP_UN_NOT);
printTSType(tsTypeAnnotationOf(node.typeAnnotation), state);
}
break;
@@ -0,0 +1,5 @@
let a: Array<!string>;
let b: Map<!string, !number>;
f<!string>();
let c = <!string>d;
type T = Array<!-1>;