-
-
Notifications
You must be signed in to change notification settings - Fork 532
anyOf should not result in intersection type #894
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
Comments
Hm, from the OpenAPI specification:
I don’t believe I think the current behavior is correct ( |
Say you want to define a parameter that accepts a Now think of this type in TypeScript: From the OpenAPI's specification, Does the explanation sound reasonable? |
Here's another example. With the current behavior: paths:
/test:
post:
requestBody:
content:
application/json:
schema:
anyOf:
- $ref: '#/components/schemas/A'
- $ref: '#/components/schemas/B'
responses:
'200':
description: ''
components:
schemas:
A:
type: object
properties:
type:
type: string
enum: ['a']
a:
type: number
required:
- type
- a
B:
type: object
properties:
type:
type: string
enum: ['b']
b:
type: string
required:
- type
- b would result in: type A = {
type: 'a',
a: number
}
type B = {
type: 'b',
b: string
}
type X = Partial<A> & Partial<B>
// testing
const x1: X = {}
const x2: X = { a: 1 } If a parameter accepts
So the current behavior is giving false positives. |
Yes I see the improvement that could be made here. That short summary I posted earlier is a bit confusing, but reading more into it, it does seem that So in digging into this a little more, I learned a little more about TypeScript union types in relation to objects, and they behave a little differently than expected. For example: type AnyOf = string | { a: string } | { b: string };
const testA: AnyOf = 'foo'; // ✅ valid
const testB: AnyOf = { a: 'foo' } // ✅ valid
const testC: AnyOf = { b: 'foo' } // ✅ valid
const testD: AnyOf = { a: 'foo', b: 'foo' } // ✅ valid It seems that TypeScript unions provide proper However, this does pose a problem for type OneOf = string | { a: string } | { b: string };
const testA: OneOf = 'foo'; // ✅ valid
const testB: OneOf = { a: 'foo' } // ✅ valid
const testC: OneOf = { b: 'foo' } // ✅ valid
const testD: OneOf = { a: 'foo', b: 'foo' } // ❌ this is valid but shouldn’t be 👆 This is current behavior, by the way, which is incorrect! I’m not sure how to solve this problem; one solution I found was to provide type OneOf = string | { a: string; b: never } | { a: never; b: string };
const testD: OneOf = { a: 'foo', b: 'foo' } // ✅ this now fails the typecheck It seems to me like Does this proposal sound reasonable? |
Yep, absolutely. Agree on both matter. And here's the PR for fixing |
Regarding the Using those types type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never }
type Xor<T, U> = T | U extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U allows you to do this type Foo = { foo: string }
type Bar = { bar: number }
type FooOrBar = Foo | Bar | string
type FooXorBar = Xor<Xor<Foo, Bar>, string> // = string | (Without<Foo, Bar> & Bar) | (Without<Bar, Foo> & Foo)
const or1: FooOrBar = { foo: 'sdf' } // ok
const or2: FooOrBar = { bar: 123 } // ok
const or3: FooOrBar = 'ddd' // ok
const or4: FooOrBar = { foo: 'sdf', bar: 123 } // ok
const or5: FooOrBar = {} // errors correctly
const xor1: FooXorBar = { foo: 'sdf' } // ok
const xor2: FooXorBar = { bar: 123 } // ok
const xor3: FooXorBar = 'ddd' // ok
const xor4: FooXorBar = { foo: 'sdf', bar: 123 } // errors correctly
const xor5: FooXorBar = {} // errors correctly Not sure how feasible it is as the |
anyOf
is equivalent toOR
, i.e. TypeScript's union|
operator.The current behavior is incorrect.
Related Note:
oneOf
is equivalent toXOR
, no direct equivalent for TypeScript. Currently treated asOR
.The text was updated successfully, but these errors were encountered: