-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
intersection of object type with known key and object type with index signature does not intersect the value types #52931
Comments
A concrete property is preferred here so the index signature is simply ignored here. Such intersections have very weird behaviors and are not that well supported by the compiler. Note that roughly this should be the equivalent of this type: interface Baz {
a: string; // Property 'a' of type 'string' is not assignable to 'string' index type '"foo"'.(2411)
[key: string]: 'foo'
} But as we can see this isn't even a valid type. I'm not sure what the answer here is. I think that perhaps it would be good to revise how such intersections play together, raise errors early, etc. Within the TS type system what you are trying to create here is invalid - the problem is that TS silently accepts it and tries to deal with it somehow. I wouldn't rely on this behavior anyhow, from my PoV it should be treated as "undefined behavior". |
Yeah, this kind of type isn't really supported, but the proposed behavior (intersecting the index signature with the known properties) would likely be a breaking change to real-world code, since people tend to use these kinds of intersection as a workaround to get a "rest" index signature... which is itself an elephant gun of a footgun but I digress @Andarist The rationale for why these intersections are allowed to exist was explained by Ryan a while back and IIRC it was something along the lines that type instantiation can't really fail per se--as in, that's not a thing that can even happen in the compiler. Furthermore TS will never produce |
i don't see why such a type should be considered invalid though.
i do agree with the current behavior of showing an error here however, as it's likely to be a mistake. but IMO it's less likely to be a mistake when it comes from an intersection of two different types like in my OP. if it helps, my use case was importing from a JSON file where its type was unfortunately widened due to #32063, but i wanted to cast it to a type without losing information from the type that came with the import. here's a simplified example, where i'm trying to get the type import foo from '../foo.json' // imported type is `{ a: string }` but the value is actually {a: 'foo'}
interface Foo {
[key: string]: 'foo'
}
const castedValue = foo as Foo
const a = castedValue['a'] // 'foo' | undefined, because the `a` key from the imported type was lost (assuming noUncheckedIndexedAccess is on)
const intersectedValue = foo as typeof foo & Foo
const b = intersectedValue['a'] // string :( |
As I said, your proposed behavior would be a breaking change in practice, since people regularly use such intersections to make types that when indexed by a string produce the index type, but also have a few disparate properties for metadata or whatever. If you're thinking "...but that's completely unsafe!", then I agree, but nonetheless it happens. See #17867 for which this kind of intersection is the very first workaround mentioned. |
if anyone's interested i made my own intersection type as a workaround: export type Intersection<T, U> = {
[K in keyof (T | U)]: Intersection<T[K], U[K]>
} & T & U
interface Foo {
a: string
}
interface Bar {
[key: string]: 'foo'
}
declare const foo: Intersection<Foo, Bar>['a'] // 'foo' i haven't tested it very much but it seems to work well for this use case editturns out it's actually more complicated than that, this doesn't work properly with unions (eg. import { Intersection } from '@detachhead/ts-helpers/dist/types/misc' |
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
I am a new TS learner I try interface Foo {
a: string
}
interface Bar {
[key: string]: 'foo'
}
type TFB = Omit<Foo & Bar, never>;
// This will cause a TypeScript error
const obj1: TFB = {
a: 'string', // Error: Type 'string' is not assignable to type 'foo'
b: 'foo'
};
const obj2: TFB = {
a: 'foo',
b: 'foo'
}; |
Bug Report
π Search Terms
intersection object type index
π Version & Regression Information
5.0.0-dev.20230223
β― Playground Link
Playground link with relevant code
π» Code
π Actual behavior
type of
foo
isstring
π Expected behavior
type of
foo
should be"foo"
The text was updated successfully, but these errors were encountered: