Closed
Description
TypeScript Version: 3.4.1 and 3.2.2
Code
interface TextChannel {
id: string;
type: 'text';
phoneNumber: string;
}
interface EmailChannel {
id: string;
type: 'email';
addres: string;
}
type Channel = TextChannel | EmailChannel;
export type ChannelType = Channel extends { type: infer R } ? R : never; // 'text' | 'email'
// Stolen from lodash.
type Omit<T, K extends keyof T> = Pick<
T,
({ [P in keyof T]: P } & { [P in K]: never } & { [x: string]: never })[keyof T]
>;
type ChannelOfType<T extends ChannelType, A = Channel> = A extends { type: T }
? A
: never;
/**
* A NewChannel is a Channel with a 'type', a 'localChannelId', with no id,
* and every other field is optional.
*/
export type NewChannel<T extends Channel> = Pick<T, 'type'> &
Partial<Omit<T, 'type' | 'id'>> & { localChannelId: string };
/**
* Create a NewChannel of the specified type, ready to hand off to, for example,
* a react form which can fill it in with values and turn it into a real Channel.
*/
export function makeNewChannel<T extends ChannelType>(type: T): NewChannel<ChannelOfType<T>> {
const localChannelId = `blahblahblah`;
return { type, localChannelId };
}
// Here's the exciting bit:
const newTextChannel = makeNewChannel('text');
// Property 'phoneNumber' does not exist on type 'NewChannel<TextChannel>'. ts(2339)
newTextChannel.phoneNumber = '613-555-1234';
const newTextChannel2 : NewChannel<TextChannel> = makeNewChannel('text');
// But this works!
newTextChannel2.phoneNumber = '613-555-1234';
Expected behavior:
Since newTextChannel
and newTextChannel2
are both of type NewChannel<TextChannel>
, I'd expect them both to have a phoneNumber
property (or both to not have a phoneNumber
property).
Actual behavior:
newTextChannel
does not have this property, and newTextChannel2
does, even though these two seem to be of the same type!
Playground Link: link
Related Issues: No