Last active
August 12, 2025 11:03
-
-
Save pesterhazy/444e51b4b52a06661c8402ff9ee4959c to your computer and use it in GitHub Desktop.
typescript interface unsoundness wat
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
| import test from "node:test"; | |
| import assert from "node:assert"; | |
| interface IExperiment { | |
| foo(opts: {}): void; | |
| } | |
| class Experiment implements IExperiment { | |
| // Wat?! | |
| // Why is Experiment allowed to implement IExperiment (the typechecker doesn't complain) | |
| // even though IExperiment.foo doesn't guarantee that opts.bar is defined? | |
| foo(opts: { bar: string }): void { | |
| // foo needs opts.bar to be there | |
| assert.ok(opts.bar); | |
| } | |
| } | |
| test.only("experiment", async () => { | |
| const experiment = new Experiment(); | |
| experiment.foo({ bar: "baz" }); | |
| }); | |
| test.only("experiment 2", async () => { | |
| let experiment: IExperiment; | |
| experiment = new Experiment(); | |
| experiment.foo({}); // oops, we called foo without passing bar, causing a runtime error | |
| }); | |
| /* | |
| How do I change the code so that I can be sure that every class that implements IExperiment | |
| is safe to use with through the interface? | |
| */ |
Ok, so the solution is (and would've never expected this) a subtle change in the interface declaration:
❌ This uses method shorthand syntax and is liable to lead to unsoundness
interface IExperiment {
foo(opts: {}): void;
}
✅ This uses object property syntax and doesn't allow methods with narrower param types to implement the interface:
interface IExperiment {
foo = (opts: {}) => void;
}
A subtle change makes a big difference in the type checks!
Thanks to Matt Pollock for pointing me in the right direction https://bsky.app/profile/mattpocock.com
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-function-parameters-bivariant
microsoft/TypeScript#46999