我只想让祖先的某些属性暴露在我的后代身上。我试图通过Pick来实现它
export class Base {
public a;
public b;
public c;
}
export class PartialDescendant extends Pick<Base, 'a' |'b'> {
public y;
}但我收到两个错误-
错误: TS2693:'Pick‘只指类型,但在这里被用作值。
和
错误: is 4020:“扩展”导出类'PartialDescendant‘的子句已经或正在使用私有名称'Pick’。
我是不是做错了什么,还有其他方法只公开基类的选定属性吗?
发布于 2018-03-22 13:39:22
参见下面的3.0解决方案
Pick只是一个类型,它不是一个类,类既是一个类型,也是一个对象构造函数。类型只存在于编译时,这就是获得错误的原因。
您可以创建一个接受构造函数的函数,并返回一个新的构造函数,该构造函数将实例化一个具有较少字段的对象(或至少声明它实例化):
export class Base {
public c: number = 0;
constructor(public a: number, public b: number) {
}
}
function pickConstructor<T extends { new (...args: any[]) : any, prototype: any }>(ctor: T)
: <TKeys extends keyof InstanceType<T>>(...keys: TKeys[]) => ReplaceInstanceType<T, Pick<InstanceType<T>, TKeys>> & { [P in keyof Omit<T, 'prototype'>] : T[P] } {
return function (keys: string) { return ctor as any };
}
export class PartialDescendant extends pickConstructor(Base)("a", "b") {
public constructor(a: number, b: number) {
super(a, b)
}
}
var r = new PartialDescendant(0,1);
type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;
type ReplaceInstanceType<T, TNewInstance> = T extends new (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F, g: infer G, h: infer H, i: infer I, j: infer J) => infer R ? (
IsValidArg<J> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => TNewInstance :
IsValidArg<I> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => TNewInstance :
IsValidArg<H> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => TNewInstance :
IsValidArg<G> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G) => TNewInstance :
IsValidArg<F> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F) => TNewInstance :
IsValidArg<E> extends true ? new (a: A, b: B, c: C, d: D, e: E) => TNewInstance :
IsValidArg<D> extends true ? new (a: A, b: B, c: C, d: D) => TNewInstance :
IsValidArg<C> extends true ? new (a: A, b: B, c: C) => TNewInstance :
IsValidArg<B> extends true ? new (a: A, b: B) => TNewInstance :
IsValidArg<A> extends true ? new (a: A) => TNewInstance :
new () => TNewInstance
) : never对于构造函数参数,您将松散参数名称、可选参数和多个签名。
编辑
自从原来的问题被回答后,打字稿改进了这个问题的可能解决方案。随着rest参数和扩展表达式中的元组的增加,我们现在不需要ReplaceReturnType的所有重载
export class Base {
public c: number = 0;
constructor(public a: number, public b: number) {
}
}
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
function pickConstructor<T extends { new (...args: any[]) : any, prototype: any }>(ctor: T)
: <TKeys extends keyof InstanceType<T>>(...keys: TKeys[]) => ReplaceInstanceType<T, Pick<InstanceType<T>, TKeys>> & { [P in keyof Omit<T, 'prototype'>] : T[P] } {
return function (keys: string| symbol | number) { return ctor as any };
}
export class PartialDescendant extends pickConstructor(Base)("a", "b") {
public constructor(a: number, b: number) {
super(a, b)
}
}
var r = new PartialDescendant(0,1);
type ArgumentTypes<T> = T extends new (... args: infer U ) => any ? U: never;
type ReplaceInstanceType<T, TNewInstance> = T extends new (...args: any[])=> any ? new (...a: ArgumentTypes<T>) => TNewInstance : never;这不仅缩短了,而且解决了许多问题。
发布于 2021-10-27 18:30:33
我在这里的游戏有点晚,但有一个替代和更短的方法来做它,如果你主要有兴趣使intellisense工作。
可以扩展基类,然后将要省略的成员重新声明为私有成员。这将生成一个类型记录错误,但是添加//@ts-忽略会清除它,不应该影响编译。
当事情很简单的时候,这是我最喜欢的方法。这里没有真正的开销,也没有挑战性的类型语法。这里唯一的缺点是在扩展类上面添加//@ts-忽略可能会阻止您接收与错误扩展基类相关的其他错误消息。
与接受的"pickConstructor“方法相比,该方法的一个优点是该方法不生成任何额外的代码。而"pickConstructor“实际上是在编译后作为一个函数存在,该函数在类定义期间运行。
class Base
{
public name:string;
}
// @ts-ignore
class Ext extends Base
{
private readonly name:undefined; // re-declare
}
let thing:Ext = new Ext();
// The line below...
// Doesn't show up in intellisense
// complains about privacy
// can't be set to anything
// can't be used as an object
thing.name = "test"; // ERRORhttps://stackoverflow.com/questions/49429913
复制相似问题