TypeScript Discriminated Union Type with default and type inference
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}
I want to create a Discriminated Union Type, where it isn't required to pass the discriminator value.
Here's my current code:
interface Single<T> {
multiple?: false // this is optional, because it should be the default
value: T
onValueChange: (value: T) => void
}
interface Multi<T> {
multiple: true
value: T
onValueChange: (value: T) => void
}
type Union<T> = Single<T> | Multi<T>
For testing I use this:
function typeIt<T>(data: Union<T>): Union<T> {
return data;
}
const a = typeIt({ // should be Single<string>
value: "foo",
onValueChange: (value) => undefined // why value is of type any?
})
const b = typeIt({ // should be Single<string>
multiple: false,
value: "foo",
onValueChange: (value) => undefined
})
const c = typeIt({ // should be Multi<string>
multiple: true,
value: ["foo"],
onValueChange: (value) => undefined
})
But I get a bunch of errors and warnings...:
In
const a
'sonValueChange
the type of the parametervalue
isany
. When settingmultiple: false
explicitly (like inconst b
) it gets correctly inferred asstring
.const c
doesn't work at all. I get this error: "Type 'string' is not assignable to type 'string'".
Do you have any idea how to solve this?
I've created a TypeScript Playground with this code
typescript generics unions discriminated-union
add a comment |
I want to create a Discriminated Union Type, where it isn't required to pass the discriminator value.
Here's my current code:
interface Single<T> {
multiple?: false // this is optional, because it should be the default
value: T
onValueChange: (value: T) => void
}
interface Multi<T> {
multiple: true
value: T
onValueChange: (value: T) => void
}
type Union<T> = Single<T> | Multi<T>
For testing I use this:
function typeIt<T>(data: Union<T>): Union<T> {
return data;
}
const a = typeIt({ // should be Single<string>
value: "foo",
onValueChange: (value) => undefined // why value is of type any?
})
const b = typeIt({ // should be Single<string>
multiple: false,
value: "foo",
onValueChange: (value) => undefined
})
const c = typeIt({ // should be Multi<string>
multiple: true,
value: ["foo"],
onValueChange: (value) => undefined
})
But I get a bunch of errors and warnings...:
In
const a
'sonValueChange
the type of the parametervalue
isany
. When settingmultiple: false
explicitly (like inconst b
) it gets correctly inferred asstring
.const c
doesn't work at all. I get this error: "Type 'string' is not assignable to type 'string'".
Do you have any idea how to solve this?
I've created a TypeScript Playground with this code
typescript generics unions discriminated-union
If you are using generic types, then you should specify the type, it also helps other developers.
– Pavlo
Jan 3 at 23:14
@Pavlo I could not disagree more. The power of TS is the ability to infer types (and I have inferred some highly complex types). Plus explicitly specifying the type basically duplicates the information which can be especially painful if the type is not explicitly named
– Titian Cernicova-Dragomir
Jan 4 at 6:46
add a comment |
I want to create a Discriminated Union Type, where it isn't required to pass the discriminator value.
Here's my current code:
interface Single<T> {
multiple?: false // this is optional, because it should be the default
value: T
onValueChange: (value: T) => void
}
interface Multi<T> {
multiple: true
value: T
onValueChange: (value: T) => void
}
type Union<T> = Single<T> | Multi<T>
For testing I use this:
function typeIt<T>(data: Union<T>): Union<T> {
return data;
}
const a = typeIt({ // should be Single<string>
value: "foo",
onValueChange: (value) => undefined // why value is of type any?
})
const b = typeIt({ // should be Single<string>
multiple: false,
value: "foo",
onValueChange: (value) => undefined
})
const c = typeIt({ // should be Multi<string>
multiple: true,
value: ["foo"],
onValueChange: (value) => undefined
})
But I get a bunch of errors and warnings...:
In
const a
'sonValueChange
the type of the parametervalue
isany
. When settingmultiple: false
explicitly (like inconst b
) it gets correctly inferred asstring
.const c
doesn't work at all. I get this error: "Type 'string' is not assignable to type 'string'".
Do you have any idea how to solve this?
I've created a TypeScript Playground with this code
typescript generics unions discriminated-union
I want to create a Discriminated Union Type, where it isn't required to pass the discriminator value.
Here's my current code:
interface Single<T> {
multiple?: false // this is optional, because it should be the default
value: T
onValueChange: (value: T) => void
}
interface Multi<T> {
multiple: true
value: T
onValueChange: (value: T) => void
}
type Union<T> = Single<T> | Multi<T>
For testing I use this:
function typeIt<T>(data: Union<T>): Union<T> {
return data;
}
const a = typeIt({ // should be Single<string>
value: "foo",
onValueChange: (value) => undefined // why value is of type any?
})
const b = typeIt({ // should be Single<string>
multiple: false,
value: "foo",
onValueChange: (value) => undefined
})
const c = typeIt({ // should be Multi<string>
multiple: true,
value: ["foo"],
onValueChange: (value) => undefined
})
But I get a bunch of errors and warnings...:
In
const a
'sonValueChange
the type of the parametervalue
isany
. When settingmultiple: false
explicitly (like inconst b
) it gets correctly inferred asstring
.const c
doesn't work at all. I get this error: "Type 'string' is not assignable to type 'string'".
Do you have any idea how to solve this?
I've created a TypeScript Playground with this code
typescript generics unions discriminated-union
typescript generics unions discriminated-union
edited Jan 3 at 22:52
Benjamin M
asked Jan 3 at 22:43
Benjamin MBenjamin M
9,3831776150
9,3831776150
If you are using generic types, then you should specify the type, it also helps other developers.
– Pavlo
Jan 3 at 23:14
@Pavlo I could not disagree more. The power of TS is the ability to infer types (and I have inferred some highly complex types). Plus explicitly specifying the type basically duplicates the information which can be especially painful if the type is not explicitly named
– Titian Cernicova-Dragomir
Jan 4 at 6:46
add a comment |
If you are using generic types, then you should specify the type, it also helps other developers.
– Pavlo
Jan 3 at 23:14
@Pavlo I could not disagree more. The power of TS is the ability to infer types (and I have inferred some highly complex types). Plus explicitly specifying the type basically duplicates the information which can be especially painful if the type is not explicitly named
– Titian Cernicova-Dragomir
Jan 4 at 6:46
If you are using generic types, then you should specify the type, it also helps other developers.
– Pavlo
Jan 3 at 23:14
If you are using generic types, then you should specify the type, it also helps other developers.
– Pavlo
Jan 3 at 23:14
@Pavlo I could not disagree more. The power of TS is the ability to infer types (and I have inferred some highly complex types). Plus explicitly specifying the type basically duplicates the information which can be especially painful if the type is not explicitly named
– Titian Cernicova-Dragomir
Jan 4 at 6:46
@Pavlo I could not disagree more. The power of TS is the ability to infer types (and I have inferred some highly complex types). Plus explicitly specifying the type basically duplicates the information which can be especially painful if the type is not explicitly named
– Titian Cernicova-Dragomir
Jan 4 at 6:46
add a comment |
1 Answer
1
active
oldest
votes
I don't think the compiler can easily infer the type of the value
parameter in the callback since the type of the object literal is still not determined when the callback is checked.
If you don't have a lot of union members a solution that works as expected is to use multiple overloads:
export interface Single<T> {
multiple?: false // this is optional, because it should be the default
value: T
onValueChange: (value: T) => void
}
interface Multi<T> {
multiple: true
value: T
onValueChange: (value: T) => void
}
type Union<T> = Single<T> | Multi<T>
function typeIt<T>(data: Single<T>): Single<T>
function typeIt<T>(data: Multi<T>): Multi<T>
function typeIt<T>(data: Union<T>): Union<T> {
return data;
}
const a = typeIt({ // is Single<string>
value: "foo",
onValueChange: (value) => undefined // value is typed as expected
})
const b = typeIt({ // is Single<string>
multiple: false,
value: "foo",
onValueChange: (value) => undefined
})
const c = typeIt({ // is be Multi<string>
multiple: true,
value: ["foo"],
onValueChange: (value) => undefined
})
Thank you. Works great!
– Benjamin M
Jan 4 at 10:22
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54030827%2ftypescript-discriminated-union-type-with-default-and-type-inference%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
I don't think the compiler can easily infer the type of the value
parameter in the callback since the type of the object literal is still not determined when the callback is checked.
If you don't have a lot of union members a solution that works as expected is to use multiple overloads:
export interface Single<T> {
multiple?: false // this is optional, because it should be the default
value: T
onValueChange: (value: T) => void
}
interface Multi<T> {
multiple: true
value: T
onValueChange: (value: T) => void
}
type Union<T> = Single<T> | Multi<T>
function typeIt<T>(data: Single<T>): Single<T>
function typeIt<T>(data: Multi<T>): Multi<T>
function typeIt<T>(data: Union<T>): Union<T> {
return data;
}
const a = typeIt({ // is Single<string>
value: "foo",
onValueChange: (value) => undefined // value is typed as expected
})
const b = typeIt({ // is Single<string>
multiple: false,
value: "foo",
onValueChange: (value) => undefined
})
const c = typeIt({ // is be Multi<string>
multiple: true,
value: ["foo"],
onValueChange: (value) => undefined
})
Thank you. Works great!
– Benjamin M
Jan 4 at 10:22
add a comment |
I don't think the compiler can easily infer the type of the value
parameter in the callback since the type of the object literal is still not determined when the callback is checked.
If you don't have a lot of union members a solution that works as expected is to use multiple overloads:
export interface Single<T> {
multiple?: false // this is optional, because it should be the default
value: T
onValueChange: (value: T) => void
}
interface Multi<T> {
multiple: true
value: T
onValueChange: (value: T) => void
}
type Union<T> = Single<T> | Multi<T>
function typeIt<T>(data: Single<T>): Single<T>
function typeIt<T>(data: Multi<T>): Multi<T>
function typeIt<T>(data: Union<T>): Union<T> {
return data;
}
const a = typeIt({ // is Single<string>
value: "foo",
onValueChange: (value) => undefined // value is typed as expected
})
const b = typeIt({ // is Single<string>
multiple: false,
value: "foo",
onValueChange: (value) => undefined
})
const c = typeIt({ // is be Multi<string>
multiple: true,
value: ["foo"],
onValueChange: (value) => undefined
})
Thank you. Works great!
– Benjamin M
Jan 4 at 10:22
add a comment |
I don't think the compiler can easily infer the type of the value
parameter in the callback since the type of the object literal is still not determined when the callback is checked.
If you don't have a lot of union members a solution that works as expected is to use multiple overloads:
export interface Single<T> {
multiple?: false // this is optional, because it should be the default
value: T
onValueChange: (value: T) => void
}
interface Multi<T> {
multiple: true
value: T
onValueChange: (value: T) => void
}
type Union<T> = Single<T> | Multi<T>
function typeIt<T>(data: Single<T>): Single<T>
function typeIt<T>(data: Multi<T>): Multi<T>
function typeIt<T>(data: Union<T>): Union<T> {
return data;
}
const a = typeIt({ // is Single<string>
value: "foo",
onValueChange: (value) => undefined // value is typed as expected
})
const b = typeIt({ // is Single<string>
multiple: false,
value: "foo",
onValueChange: (value) => undefined
})
const c = typeIt({ // is be Multi<string>
multiple: true,
value: ["foo"],
onValueChange: (value) => undefined
})
I don't think the compiler can easily infer the type of the value
parameter in the callback since the type of the object literal is still not determined when the callback is checked.
If you don't have a lot of union members a solution that works as expected is to use multiple overloads:
export interface Single<T> {
multiple?: false // this is optional, because it should be the default
value: T
onValueChange: (value: T) => void
}
interface Multi<T> {
multiple: true
value: T
onValueChange: (value: T) => void
}
type Union<T> = Single<T> | Multi<T>
function typeIt<T>(data: Single<T>): Single<T>
function typeIt<T>(data: Multi<T>): Multi<T>
function typeIt<T>(data: Union<T>): Union<T> {
return data;
}
const a = typeIt({ // is Single<string>
value: "foo",
onValueChange: (value) => undefined // value is typed as expected
})
const b = typeIt({ // is Single<string>
multiple: false,
value: "foo",
onValueChange: (value) => undefined
})
const c = typeIt({ // is be Multi<string>
multiple: true,
value: ["foo"],
onValueChange: (value) => undefined
})
answered Jan 4 at 6:54
Titian Cernicova-DragomirTitian Cernicova-Dragomir
73.1k35270
73.1k35270
Thank you. Works great!
– Benjamin M
Jan 4 at 10:22
add a comment |
Thank you. Works great!
– Benjamin M
Jan 4 at 10:22
Thank you. Works great!
– Benjamin M
Jan 4 at 10:22
Thank you. Works great!
– Benjamin M
Jan 4 at 10:22
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54030827%2ftypescript-discriminated-union-type-with-default-and-type-inference%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
If you are using generic types, then you should specify the type, it also helps other developers.
– Pavlo
Jan 3 at 23:14
@Pavlo I could not disagree more. The power of TS is the ability to infer types (and I have inferred some highly complex types). Plus explicitly specifying the type basically duplicates the information which can be especially painful if the type is not explicitly named
– Titian Cernicova-Dragomir
Jan 4 at 6:46