Вы можете изменить параметр типа T
, чтобы он всегда был полностью вложенным типом:
class Identity<T> {
private readonly value: T | Identity<T>;
constructor(value: Identity<T>);
constructor(value: T);
constructor(value: any) {
this.value = value;
}
static of<T>(value: Identity<T>): Identity<T>;
static of<T>(valud: T): Identity<T>;
static of<T>(value: T | Identity<T>) {
return new Identity(value);
}
join(): T {
if (this.value instanceof Identity) {
return this.value.join();
}
return this.value;
}
}
const one = Identity.of(Identity.of(Identity.of(123)));
const result: number = one.join();
const result2 = new Identity(new Identity(new Identity(321)));
// Identity<number>
Я бы предложил вышеописанный метод, так как он, по-видимому, обеспечивает ту функциональность, которая вам действительно нужна.
Если вам нужно, чтобы тип T
действительно был вложенным Identity<Identity<...>>
, то все еще возможно, если бы value
был общедоступным, мы могли бы сделать следующее:
class Identity<T> {
public readonly value: T;
constructor(value: T) {
this.value = value;
}
static of<T>(value: T): Identity<T> {
return new Identity(value);
}
public join(): IdentityJoinResult<T> {
if (this.value instanceof Identity) {
return this.value.join();
}
return this.value as IdentityJoinResult<T>;
}
}
type IdentityJoinResult<T> =
T extends Identity<any>
? { [Key in keyof T]: IdentityJoinResult<T[Key]> }["value"]
: T
;
const one = Identity.of(Identity.of(Identity.of(123)));
const result: number = one.join();
const result2 = new Identity(new Identity(new Identity(321))).join();
Однако еслиизменив value
на приватный, вы заметите, что вы больше не можете индексировать по «значению» в сигнатуре типа, поэтому последнее, что мы можем сделать, чтобы это исправить, - это перенести нашу логику типов на отдельный открытый интерфейс, которыймы можем определить по типу:
class Identity<T> {
private readonly value: T;
constructor(value: T) {
this.value = value;
}
static of<T>(value: T): Identity<T> {
return new Identity(value);
}
public join(): IdentityJoinResult<T> {
if (this.value instanceof Identity) {
return this.value.join();
}
return this.value as IdentityJoinResult<T>;
}
}
interface NestedIdentity<T> {
_value: T extends Identity<infer U> ? NestedIdentity<U> : T;
}
type NestedIdentityValue<T> =
T extends NestedIdentity<any>
? { [Key in keyof T]: NestedIdentityValue<T[Key]> }["_value"]
: T
;
type IdentityJoinResult<T> = NestedIdentityValue<NestedIdentity<T>>;
const one = Identity.of(Identity.of(Identity.of(123)));
const result: number = one.join();
const result2 = new Identity(new Identity(new Identity(321))).join();