
[英]What is different between Pick<T, “properties”> and T[“properties”] in typescript
[英]Why doesn't TypeScript pick up superclass properties?
这段代码的目的是提供一种类型安全的方法来将 mixin 应用于类。 它还可以防止重复的 mixin。 该代码取决于ts-toolbelt 。 它分为多个文件,但为了简单起见,我在这里将它包含在一个块中。
这是混合代码。
//////////////////////////////
// ts-toolbelt
//////////////////////////////
type List<A = any> = ReadonlyArray<A>;
type Class<P extends List = any[], R extends object = object> = { new (...args: P): R };
type Function<P extends List = any, R extends any = any> = (...args: P) => R;
// In following code,
// `L.List` refers to ts-toolbelt's `List`,
// `C.Class` refers to ts-toolbelt's `Class`, and
// `F.Function` refers to ts-toolbel's `Function`.
//////////////////////////////
// Utilities
//////////////////////////////
/**
* Predicate function with varying parameters.
*/
export type VarPredicate<T extends L.List = any> = F.Function<T, boolean>;
/**
* Predicate function with one parameter.
*/
export type Predicate<T> = VarPredicate<[T]>;
/**
* Gets the prototype chain of an object.
*
* @template T
* @param {T} obj The object.
* @param {Predicate<any>} stopAfterPredicate If returns `true`, the function will stop searching for more prototypes and return.
*
* @returns {readonly any[]} An array of each prototype. Starts with `obj`.
*/
// The `stopAfterPredicate` parameter is a bit messy, but exists for performance.
function getPrototypeChain<T extends object>(obj: T, stopAfterPredicate?: Predicate<any>): readonly any[] {
const chain: any[] = [];
while(obj) {
chain.push(obj);
if(stopAfterPredicate && stopAfterPredicate(obj)) break;
obj = Object.getPrototypeOf(obj);
}
return chain;
}
//////////////////////////////
// Mixin
//////////////////////////////
/**
* Represents a mixin.
*/
// This could be simplified to a `type`? Any reasons why it should stay an `interface`?
interface Mixin<T extends C.Class, R extends C.Class, A extends L.List = any[]> {
(ctor: T, ...args: A): T & R;
}
/**
* Represents a class that has a mixin or mixins applied to it.
*/
export default interface Mixed<T extends C.Class, A extends L.List = any[]> {
/**
* Applies a mixin to `this`.
*
* @template T
* @template R
* @param {Mixin<T, R>} mixin The mixin.
*
* @return {Mixed<ReturnType<Mixin<T, R>>> & ReturnType<Mixin<T, R>>} A new class to which `mixin` is applied.
*/
with<R extends C.Class>(mixin: Mixin<T, R>, ...args: A): Mixed<ReturnType<Mixin<T, R>>> & ReturnType<Mixin<T, R>>;
}
/**
* Tracked applied mixins.
*/
const appliedMixins = new WeakMap();
/**
* Checks if a mixin was already applied to a class.
*
* @template T
* @template R
* @param {Mixin<T, R>} mixin The mixin.
* @param {C.Class} superClass The class.
*
* @return {boolean} `true`, if `mixin` was already applied to `superClass`.
* `false`, otherwise.
*/
function wasApplied<T extends C.Class, R extends C.Class>(mixin: Mixin<T, R>, superClass: C.Class): boolean {
// Get to which `mixin` is already applied.
const entries = appliedMixins.get(mixin);
// If no value in the map was set for the key `mixin`,
// then we know that the mixin has not been applied to any classes.
if(!entries) return false;
// Check if the mixin was already applied to `superClass`.
let result: boolean = false;
getPrototypeChain(superClass, proto => (result = entries.has(proto)));
return result;
}
/**
* Tracks that a mixin was applied to a class.
*
* @template T
* @template R
* @param {Mixin<T, R>} mixin The mixin.
* @param {C.Class} superClass The class.
*/
function applyMixin<T extends C.Class, R extends C.Class>(mixin: Mixin<T, R>, superClass: C.Class): void {
// Get to which `mixin` is already applied.
let entries = appliedMixins.get(mixin);
// If no value in the map was set for the key `mixin`,
// then create a new set for it.
if(!entries) {
entries = new WeakSet();
appliedMixins.set(mixin, entries);
}
// Track that `mixin` was applied to `superClass`.
entries.add(superClass);
}
/**
* Mixin builder.
*/
class MixinBuilder<T extends C.Class> {
public constructor(superClass: T) {
this.superClass = superClass;
}
private readonly superClass: T;
/**
* See {@link Mixed#with} for docs.
* This function does not permit duplicates.
*/
with<R extends C.Class>(mixin: Mixin<T, R>, ...args: A): Mixed<ReturnType<Mixin<T, R>>> & ReturnType<Mixin<T, R>> {
if(wasApplied(mixin, this.superClass)) return (this.superClass as Mixed<ReturnType<Mixin<T, R>>> & ReturnType<Mixin<T, R>>);
const mixed = mixin(this.superClass, args);
applyMixin(mixin, mixed);
return (class extends mixed {
static with<K extends C.Class>(mixin: Mixin<typeof mixed, K>, ...args: A) {
return new MixinBuilder(mixed).with(mixin, args);
}
} as any);
}
}
function mix<T extends object>(superClass: C.Class<any[], T>): MixinBuilder<C.Class<any[], T>> {
return new MixinBuilder(superClass);
}
这是使用上述代码的示例混合。
/**
* Public declarations for elements that have the `disabled` property.
*/
interface DisabledStateInterface {
/**
* The `disabled` property.
*/
disabled: boolean;
/**
* Function is called when the element is clicked.
*
* @implSpec Super should not be called if {@link disabled} is `true`.
*/
click(): void;
}
/**
* Declarations for elements that have the `disabled` property.
*/
class DisabledStateClass {
/**
* Sets the `aria-disabled` attribute.
*
* @param {boolean} disabled The value of the `disabled` property.
*
* @protected
*/
protected _setAriaDisabled?(disabled: boolean): void;
}
/**
* Mixin that provides the `disabled` property and sets the `aria-disabled` attribute accordingly.
*
* @template T
* @param {T} superClass The superclass.
*
* @return {ReturnType<Mixin<T, C.Class<any[], DisabledStateInterface>>>}
*
* @constructor
*/
function DisabledStateMixin<T extends C.Class<any[], LitElement & DisabledStateClass>>(
superClass: T
): ReturnType<Mixin<T, C.Class<any[], DisabledStateInterface>>> {
class DisabledState extends superClass {
@property({type: Boolean, reflect: true})
public disabled: boolean = false;
protected override _setAriaDisabled(disabled: boolean): void {
this._ariaDisabledElement.setAttribute('aria-disabled', disabled.toString());
}
protected override updated(properties: PropertyValues): void {
super.updated(properties);
// If `disabled` was updated, set the `aria-disabled` attribute accordingly.
if(properties.has('disabled')) this._setAriaDisabled(this.disabled);
}
/**
* Gets the element to which to apply the `aria-disabled` attribute.
*
* By default, returns `this`.
*
* @returns {HTMLElement} The element to which to apply the `aria-disabled` attribute.
*
* @protected
*/
protected get _ariaDisabledElement(): HTMLElement {
return this;
}
}
return DisabledState;
}
最后,这是一个示例用法。
class MyElement extends mix(LitElement).with(DisabledStateMixin) {
// This line reports TS4117.
// Says that `styles` is not declared in `LitElement & DisabledStateInterface`.
// This is not true. `styles` is declared in `LitElement`.
public static override styles: CSSResult[] = ...;
// However, this does not report an error.
protected override render(): HTMLTemplateResult {
return html`
Hello, World!
`;
}
}
有关我的问题,请参阅上述代码块中的注释。
感谢您的时间!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.