Repository navigation
Compatibility: identical generic addElement methods rejected (Polotno TS2719, matches upstream pin) #25
Description
Summary
Two independently written, identical generic method signatures become incompatible when the parameter intersects a discriminant with a distributive conditional/Extract/Omit type. This is a dependency-free reduction of a Polotno PageType assignment that fails with TS2719 in tsc-rs and the exact upstream pin. The small reduction reports TS2322 while retaining the same addElement parameter comparison failure.
This is a compatibility report against TypeScript 7.0.2, not evidence of a Rust-only bug. The exact upstream version documented by this project, typescript@7.1.0-dev.20260929.1 / 673a5f17d713, produces the same diagnostic. I am filing this to record a real-world upgrade difference in this repository.
Versions and platform
- tsc-rs npm release: 0.1.0.
- Baseline: TypeScript 7.0.2.
- Upstream comparison: TypeScript 7.1.0-dev.20260929.1.
- macOS:
Darwin 25.6.0 arm64 arm.
Standalone reproduction
No application source or external library is needed for this small repro; the package.json only installs comparison compilers.
package.json
{
"private": true,
"devDependencies": {
"tsc7": "npm:typescript@7.0.2",
"upstream": "npm:typescript@7.1.0-dev.20260929.1",
"tsc-rs": "0.1.0"
}
}tsconfig.json
{
"compilerOptions": {
"target": "es2022",
"module": "preserve",
"moduleResolution": "bundler",
"lib": [
"esnext",
"dom",
"dom.iterable"
],
"strict": true,
"skipLibCheck": true,
"noEmit": true,
"types": []
},
"files": [
"case.ts"
]
}case.ts
type ElementType =
| { type: "text"; text: string }
| { type: "image"; src: string };
interface Page {
addElement<T extends ElementType["type"] | (string & {})>(
attrs: { type: T } & (T extends ElementType["type"]
? Partial<Omit<Extract<ElementType, { type: T }>, "type">>
: Record<string, any>),
): void;
}
declare const returned: {
addElement<T extends ElementType["type"] | (string & {})>(
attrs: { type: T } & (T extends ElementType["type"]
? Partial<Omit<Extract<ElementType, { type: T }>, "type">>
: Record<string, any>),
): void;
};
export const page: Page = returned;bun install --ignore-scripts
bun node_modules/tsc-rs/bin/tsc-rs -p tsconfig.json --pretty false
bun node_modules/tsc7/bin/tsc -p tsconfig.json --pretty false
bun node_modules/upstream/bin/tsc -p tsconfig.json --pretty falseResults
| Checker | Result |
|---|---|
| TypeScript 7.0.2 | Pass |
| tsc-rs 0.1.0 | Fail, exit 2 |
| TypeScript 7.1.0-dev.20260929.1 (exact upstream pin) | Fail, exit 2 |
| Bun 1.4.3-canary.1+43a42a836 | Pass |
tsc-rs output (upstream reports the same failure):
case.ts(18,14): error TS2322: Type '{ addElement<T extends ElementType["type"] | (string & {})>(attrs: { type: T; } & (T extends "image" | "text" ? Partial<Omit<Extract<...> | Extract<...>, "type">> : Record<...>)): void; }' is not assignable to type 'Page'.
Types of property 'addElement' are incompatible.
Type '<T extends ElementType["type"] | (string & {})>(attrs: { type: T; } & (T extends "image" | "text" ? Partial<Omit<Extract<{ type: "text"; text: string; }, { type: T; }> | Extract<{ type: "image"; src: string; }, { ...; }>, "type">> : Record<...>)) => void' is not assignable to type '<T extends ElementType["type"] | (string & {})>(attrs: { type: T; } & (T extends "image" | "text" ? Partial<Omit<Extract<{ type: "text"; text: string; }, { type: T; }> | Extract<{ type: "image"; src: string; }, { ...; }>, "type">> : Record<...>)) => void'. Two different types with this name exist, but they are unrelated.
Types of parameters 'attrs' and 'attrs' are incompatible.
Type '{ type: T; } & (T extends "image" | "text" ? Partial<Omit<Extract<{ type: "text"; text: string; }, { type: T; }> | Extract<{ type: "image"; src: string; }, { type: T; }>, "type">> : Record<...>)' is not assignable to type '{ type: T; } & (T extends "image" | "text" ? Partial<Omit<Extract<{ type: "text"; text: string; }, { type: T; }> | Extract<{ type: "image"; src: string; }, { type: T; }>, "type">> : Record<...>)'. Two different types with this name exist, but they are unrelated.
Type '{ type: T; } & (T extends "image" | "text" ? Partial<Omit<Extract<...> | Extract<...>, "type">> : Record<...>)' is not assignable to type 'T extends "image" | "text" ? Partial<Omit<Extract<{ type: "text"; text: string; }, { ...; }> | Extract<...>, "type">> : Record<...>'.
Expected behavior / attribution
The identical signatures are accepted by TypeScript 7.0.2 and Bun. Pinned upstream rejects them too, so this tracks an inherited generic structural-comparison problem rather than a Rust-only discrepancy.
Controls and upstream context
A strong matching upstream report is microsoft/TypeScript#64673. It concerns identical generic signatures with distributive conditional parameter types. Proposed fix microsoft/TypeScript#64307 is unmerged at the time checked. I have not built that patch or verified it against these cases.
For the original library form, add polotno@4.13.0 and replace case.ts with:
import { createStore } from 'polotno/model/store';
import type { PageType } from 'polotno/model/page-model';
const store = createStore({ key: '', showCredit: false });
export const page: PageType = store.addPage();I also verified this four-line case in a fresh external install. Polotno 4.13.0 uses mobx-state-tree 8.0.0. TypeScript 7.0.2 and Bun pass; tsc-rs and its upstream pin report TS2719, tracing the incompatibility to addElement. These are types from the same installed library packages, not evidence of duplicate package versions.
Diagnostic controls:
- Assigning the original
store.addPage()result toOmit<PageType, 'addElement'>passes in all compilers. This removes part of the contract and is only an isolation control. - Letting the original page type be inferred passes.
- In the small repro, the return type was simplified to void and the failure remained, localizing it to parameter comparison.
Activity
t3dotgg commentedon Oct 8, 2026
Note
🤖 Claude Opus 5.5 responding on behalf of Theo
Thanks for the report and the dependency-free reduction, @jakeleventhal.
Confirmed: tsc-rs and
tscbuilt at the pinned revision, microsoft/TypeScript673a5f17d713, give the same TS2322 on the reduction, byte for byte, so it is upstream behavior and not a port bug. tsc-rs output follows the pin, and an upstream change reaches tsc-rs with a later pin bump.We are labeling this
upstream issueand keeping it open while we look for the upstream change and any upstream issue; we will link them here. If you open one in microsoft/TypeScript, please link it too.jakeleventhal commentedon Oct 8, 2026
Thanks for confirming. One existing upstream candidate to check is microsoft/TypeScript#64673: it reports identical generic signatures failing to relate when their parameter types contain distributive conditionals. It links microsoft/TypeScript#64307, which is still open and unmerged; the issue author reports that building that PR fixes their repro.
I haven't built or tested that patch against our reduced
addElementcase or the original Polotno assignment, so this is a strong candidate rather than a confirmed duplicate. It may be worth checking that patch against these cases before opening another upstream issue.t3dotgg commentedon Oct 8, 2026
Note
🤖 Claude Opus 5.5 responding on behalf of Theo
Thanks for the pointer. We checked it, and it is the same bug.
typescript@7.1.0-dev.20260917.1.7.1.0-dev.20260916.1and 7.0.2 pass.tsc-rs follows the pin, so the fix reaches tsc-rs with the first pin bump after it merges. We are keeping this open to track #64673. Until then, letting the page type be inferred (your control) avoids the error.