Включить проверку типов и отвергать неверно типизированные решения MyMap - #358
Merged
Conversation
vitest strips types with esbuild, so the type assertions in lesson tests were never executed: expectTypeOf() in 21 lessons and @ts-expect-error in 3 lessons passed no matter what the lesson exported. Nothing invoked tsc at all. bin/test2.sh now type-checks the lesson it runs. That script, not make check, is what the platform runs against a student's solution, so it is the only place that can reject a wrongly typed one. It checks a single lesson and runs the compiler concurrently with the tests, because the platform kills a lesson run after six seconds; that keeps a run at about two seconds instead of four. Output is buffered so a type error is reported on its own rather than under runtime output that passed only because the types were erased. make type-check covers the whole course in one pass and hangs off make test, so CI gets a fast aggregate signal before the 54 per-lesson runs. Two latent errors surfaced once tsc actually ran, both in config: moduleResolution node10 is a hard error in TypeScript 6, and 6.0 no longer enumerates node_modules/@types by default, which broke the ambient Node types the hello-world lesson relies on. vitest.config.ts also had to import defineConfig from vitest/config; via vite the test key is an excess property. No lesson content needed fixing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#268) The test only ever instantiated MyMap<string, number>, so an implementation that ignored K and V and hardcoded string and number satisfied it — the case reported in the issue. toExtend was also too loose to notice a get() that never returns undefined, or a type built entirely out of any. Instantiating the type a second time with an unrelated pair, number and string[], is what catches hardcoding; it matches how the neighbouring generic-types lesson already proves a type is generic. toEqualTypeOf replaces toExtend so that a missing undefined and a blanket any are rejected too. EXERCISE.md now states what the tightened test requires and previously only implied: that the type has to work with any combination of the two parameters, that get() yields undefined for a missing key, and that the Map lives in a property named values — a student naming it otherwise had no way to know. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A trap that only cleaned up ran on INT and TERM and then let the script resume: it deleted the logs while tsc and vitest were still writing, then read them back empty and exited with vitest's status. On a local Ctrl-C that turned a killed run into a silent pass, and made the script uninterruptible. Each signal handler now exits, and cleanup also reaps a still-running tsc. Also address review notes: an explicit mktemp template instead of the deprecated -t, a compose-type-check wrapper to match the other checks, the tsconfig settings the compiler steps depend on written down in README.md, and the rationale kept in one place rather than repeated in three. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #344
Closes #268
Зачем
Проверка типов в курсе не выполнялась вообще.
vitestстирает типы через esbuild, аtscне вызывался нигде, поэтомуexpectTypeOfв 21 уроке и@ts-expect-errorв 3 уроках проходили при любом содержимом урока. Из-за этого в уроке «Дженерики с несколькими параметрами» реализацияMyMapс неверными типами принималась как верная (#268).Что сделано
Инфраструктура (#344)
bin/test2.shпроверяет типы урока, который запускает. Именно этим скриптом платформа проверяет решение студента, поэтому это единственное место, где неверно типизированное решение можно отвергнуть.make type-check(tsc --noEmit) покрывает весь курс и подвешен кmake test, так чтоmake checkи CI получают быстрый агрегированный сигнал до 54 прогонов уроков. Добавлена обёрткаcompose-type-check.tsconfig.jsonдля этого обязательны.Урок 47 (#268)
MyMap<number, string[]>) ловит захардкоженные типы — так же, как это уже сделано в соседнем20-generic-types.toEqualTypeOfвместоtoExtend, потому чтоtoExtendпропускал и отсутствующийundefined, и сплошнойany.{ru,en}/EXERCISE.mdтеперь называет то, что тест требует, а текст раньше только подразумевал: что тип должен работать с любым сочетанием параметров, чтоget()возвращаетundefinedдля отсутствующего ключа и чтоMapлежит в свойствеvalues— последнее студенту было неоткуда угадать.Попутно всплыло
Как только
tscреально запустился, обнаружились две скрытые ошибки конфигурации.moduleResolution: "node"— жёсткая ошибка в TypeScript 6, а TS 6 больше не подключаетnode_modules/@typesавтоматически, из-за чего отвалились ambient-типы Node, на которых держится урок hello-world. Плюсvitest.config.tsимпортировалdefineConfigизvite, где ключtest— лишнее свойство. Контент уроков править не понадобилось: все 21 урок сexpectTypeOfи все 3 с@ts-expect-errorоказались типо-чистыми.Замечание к формулировке #344
Вариант 2 в issue описан неверно: там сказано, что обычный
tsc --noEmitоставитexpectTypeOfпустышкой. Матчерыexpect-typeпроверяются исключительно на этапе компиляции (toExtend: <Expected extends …>(...MISMATCH: MismatchArgs<…>) => true), поэтомуtscих полноценно выполняет, а--typecheckу vitest меняет только формат отчёта. Отсюда выбор в пользуtsc.Ограничение, которого не было в issue
Платформа запускает каждую проверку под
timeout 6, а таймаут показывается студенту как зависшая программа с пустым выводом (lesson_tester.rb:exitstatus == 124→failed-infinity, вывод обнуляется). Последовательныйtscпо всему проекту давал 3–5 с сверх тестов и один раз из пяти пробивал лимит. Поэтому проверка ограничена одним уроком и идёт параллельно сvitest, а вывод буферизуется, чтобы ошибка типов докладывалась отдельно, а не под рантайм-выводом, который прошёл только потому, что типы стёрты.Проверка
Точной командой платформы, с подменой
index.tsчерез bind-mount (typescript 6.0.3, vitest 4.1.7):string/number(случай из #268)get(key: K): VбезundefinedanyвездеТакже: рантайм-падение по-прежнему показывает студенту полный диф
vitest;SIGTERMзавершает раннер с кодом 143, не резюмируя выполнение.Тайминги: прогон урока 1,3 с → 2,0 с (при лимите 6 с),
make test62 с → 95 с,make checkзелёный за 2 м 36 с.Не входит в этот PR
Ужесточение матчеров в остальных 20 уроках — вынесено в #357 с замерами: 28 из 30 эталонов такую замену переживают, но тексты заданий типы не фиксируют, поэтому нужна поурочная правка формулировок. Оба issue эту работу из объёма исключали.