Comments (7)
Not a bug. Types are narrowed on assignment, so TS knows it's impossible for that else
to execute and test
gets narrowed to never
. Hence the error message
Property 'c' does not exist on type 'never'.
from typescript.
The simplest form looks like this:
type Thing<T> =
| { op: 'a', a: T }
| { op: 'c' | 'd', cd: T };
function test(value: Thing<number | string>): void {
if (value.op === 'c' || value.op === 'd') {
// this is fine
value.op
value.cd
} else {
// ts knows value.op can only be 'a'
value.op
// ^?
// but no a property
value.a
}
}
This is a duplicate of #31404 but I'll ping some folks to take another bite at the apple; maybe it's more tractable these days
from typescript.
The TL;DR is that from the control flow analyzer's perspective, the code looks like this
type Thing<T> =
| { op: 'a', a: T }
| { op: 'c' | 'd', cd: T };
function test(value: Thing<number | string>): void {
if (value.op === 'c') {
// this is fine
value.op
value.cd
} else if (value.op === 'd') {
// this is fine
value.op
value.cd
} else {
// ts knows value.op can only be 'a'
value.op
// ^?
// but no a property
value.a
}
}
By exclusion, value.op
went from the total possible union 'a' | 'c' | 'd'
to a
, but the object union itself couldn't undergo any narrowing because there's never a distinct point at which we can remove { op: 'c' | 'd', cd: T }
from the list of possible inhabitants of value
from typescript.
Oh, I've provided a bad example here. It still happens when the value is not known (in a function) for me in some places
It seems to happen when there are nested type unions?
type Thing<T> = {
op: 'a' | 'b'
v: T
} | {
op: 'c' | 'd'
v1: T
v2: T
} | T | T[];
function test(columns: { value: Thing<number | string> | undefined | null }[]): void {
for (const { value } of columns) {
if (value instanceof Array) {
// this is fine
} else if (typeof value == 'object' && value != null) {
if (value.op == 'c' || value.op == 'd') {
// this is fine
value.op
value.v1
value.v2
} else {
// ts knows value.op can only be 'a' | 'b'
if (value.op == 'c' || value.op == 'd') value
// but v is still not defined
value.v
}
} else if (value != null) {
// string | number
value
}
}
}
from typescript.
That's a much more instructive example, thanks. I'd recommend replacing the example in the OP with it since it perfectly illustrates the actual problem you're facing.
It seems like the addition of extra union members causes TS to no longer treat Thing
as a discriminated union: from the example it's easy to see that TS narrows value.op
to "a" | "b"
(hence the first error) but not value
itself (hence the second).
edit: That second paragraph is probably wrong. See Ryan's reply below.
from typescript.
Ah, so it's just the union-typed discriminant that's throwing it off? I figured it was because value
was initially typed as <discriminated union> | T | T[] | null | undefined
that it wasn't triggering the discriminated union checks.
from typescript.
This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes.
from typescript.
Related Issues (20)
- `Uint8Array.prototype.constructor` is missing/incorrect type, and probably much more HOT 8
- Template Literal Type JSX children prop issue HOT 1
- [isolatedDeclarations] Add a syntactic form of computed property name which is always emitted as a computed property name HOT 4
- "Move to file" on global code unnecessarily imports/exports, generates invalid code HOT 3
- Wrong type on when using logical operators in nested conditions HOT 3
- Narrowing for constant indexed access fails on array getter HOT 1
- Design Meeting Notes, 6/7/2024 HOT 6
- Typescript 5.5 performance regression on arktype HOT 2
- Assertion violation in "move to new file"
- [nightly] Syntactically invalid declaration files emitted under extremely specific conditions HOT 1
- Make TypeScript Compilable to Native Machine Code! HOT 3
- [NewErrors] 5.6.0-dev.20240609 vs 5.4.5 HOT 58
- [ServerErrors][JavaScript] 5.6.0-dev.20240609 HOT 14
- [ServerErrors][TypeScript] 5.6.0-dev.20240609 HOT 24
- Special member completions not work after a comment.
- Module resolution: ESM-style namespace importing is broken HOT 5
- getSpecifierForModuleSymbol failing in 5.5 rc
- Typescript process is consuming much memory and preventing suggestions at editor HOT 2
- Ignored tsconfig when targeting file HOT 2
- Control flow analysis for element access with variable index doesn't account for async race condition HOT 7
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from typescript.