When you do a union like that - you end up with T being only types which are assignable to both TvChannel and RadioChannel. From your inheritance setup this is only going to be Channel.
You can handle this kind of behaviour with type guards - https://www.typescriptlang.org/docs/handbook/advanced-types.html - you use a type guard to assert that your type is actually of one type or the other.
However, based on your code, I don't think this is the exact behaviour you're after.
You seem to want a union of the types - where properties that are unique to a specific type are optional, and the ones that are shared are as they are defined in Channel.
Here's how I'd type this. I've added property foo to RadioChannel to test that it's behaving correctly
Types:
export interface Channel {
id: number;
name: string;
}
export interface TvChannel extends Channel {
subscribed: boolean;
}
export interface RadioChannel extends Channel {
// marker interface to distinguish radio channels from TV channels
foo: string;
}
Helper types. The idea here is to construct two sets of keys. UniqueKeys<T, U> is the set of keys which only exist on one object or the other, but not on both. CommonKeys<T, U> is the opposite.
type UniqueKeys<T, U> = Exclude<keyof T, keyof U> | Exclude<keyof U, keyof T>;
// Test unique keys
type ChannelUniqueKeys = UniqueKeys<TvChannel, RadioChannel>; // "subscribed" | "foo"
type CommonKeys<T, U> = keyof T & keyof U;
// Test common keys
type ChannelCommonKeys = CommonKeys<TvChannel, RadioChannel>; // "id" | "name"
Now we reconstruct these back into object slices based on these two keysets, looking up the correct value from (T & U) - the combination of both types.
type UniqueObjectSlice<T, U> = {[key in UniqueKeys<T, U>]: (T & U)[key]};
type CommonObjectSlice<T, U> = {[key in CommonKeys<T, U>]: (T & U)[key]};
Finally we can construct PartialUnion<T, U>. This is now simply the Partial (make all properties optional) of the unique object slice, combined with the common object slice:
type PartialUnion<T, U> = Partial<UniqueObjectSlice<T, U>> & CommonObjectSlice<T, U>;
I've attached this to a function just to check it's working correctly - but just sub myComponent for your class and you should get the intended behaviour.
function myComponent<T extends PartialUnion<TvChannel, RadioChannel>>(properties: T) {
// Unique keys
type foo = typeof properties.foo; // string | undefined
type subscribed = typeof properties.subscribed; // boolean | undefined
// Common keys
type id = typeof properties.id; // number
type name = typeof properties.name; // string
}
Playground link