TypeScript에서 강력한 유형 프롬프트로 알림 트리 만들기
31353 단어 typescriptwebdev
약 1년 반 전에 위에서 이야기한 패키지를 게시했습니다. red-manager . 그러나 몇 가지 단점이 있습니다.
이러한 문제의 원인은 자바스크립트 파일에서 마이그레이션되었기 때문입니다.
이제 더 강력하게 만들기 위해 문자열 리터럴 유니온 유형을 사용하여 유형 프롬프트를 강화하고 동시에 코드를 더 간단하게 만듭니다.
먼저 어떻게 보이는지 봅시다:
// define the root node at first.
const root = NotificationTree.root(['notification', 'me']);
나머지 코드.
const notification = root.expand('notification', ['comments', 'likes', 'something']);
notification.getChild('likes').setValue(20);
const something = notification.getChild('something');
something.setValue(10);
root.dump();
dump
함수는 콘솔에서 트리를 인쇄할 수 있습니다. 다음과 같습니다.이 클래스의 일반 정의는 다음과 같습니다.
class NotificationTree<Prev extends string = string, Curr extends string = string> {
// ...
}
Prev
는 조상 경로를 나타내고 Curr
는 자식 이름을 나타냅니다. 아마도 이 설명은 그리 직관적이지 않을 것입니다. root
의 유형 서명에 대해 살펴보겠습니다.const root: NotificationTree<"@ROOT", "notification" | "me">
루트 노드로서 첫 번째 일반
"@ROOT"
은 해당 이름입니다. 두 번째 일반: "notification" | "me"
는 모든 하위 이름을 포함하는 공용체 유형입니다.루트 노드를 정의한 후
expand
함수를 사용하여 "트리를 성장"시킵니다. IntelliSense는 첫 번째 매개 변수를 입력하는 동안 확장할 수 있는 모든 분기 이름을 제공하고 제한합니다. 우리는 오타를 두려워하지 않을 것입니다 :)const notification = root.expand('notification', ['comments', 'likes', 'something']);
두 번째 매개변수는 "알림"하위 트리의 모든 하위 이름을 나타내는 문자열 배열입니다. 타입 시그니쳐를 보자.
const notification: NotificationTree<"@ROOT/notification", "comments" | "likes" | "something">
루트의 시그니쳐와 비슷해 보이지만 차이점은 첫 번째 제네릭 타입이 템플릿 문자열이고 값이 조상에서 자기 자신으로의 경로라는 점입니다.
전체 코드:
This file is written in
wepo-project/web-client
, if you are interested, welcome to check it out!
const ROOT_NAME = "@ROOT";
type SPLIT = "/";
type JoinType<A extends string, B extends string> = `${A}${SPLIT}${B}`
type Callback = (val: number) => void;
export type ExtractType<Type> = Type extends NotificationTree<infer P, infer C> ? NotificationTree<P, C> : never;
export type ExtractPrev<Type> = Type extends NotificationTree<infer P, string> ? P : never;
export type ExtractCurr<Type> = Type extends NotificationTree<string, infer C> ? C : never;
/**
* NotificationTree
* The `ROOT` instance can only be instantiated once.
* Prev: ancestors path
* Curr: children names
*/
export class NotificationTree<Prev extends string = string, Curr extends string = string> {
static ROOT: NotificationTree | null = null;
name: string;
private parent: NotificationTree | null;
private children: Record<Curr, NotificationTree>;
private _value: number = 0;
private listener: Map<Callback, { target: any, once: boolean }>;
/**
* Create an root node of NotificationTree
* also can create by constructor
* @returns Root Node
*/
static root<Name extends string = string>(children: Name[]): NotificationTree<typeof ROOT_NAME, Name> {
if (this.ROOT) {
console.error(`can not create root node duplicatly`)
return this.ROOT
} else {
const _root = new NotificationTree(null, ROOT_NAME, children);
this.ROOT = _root;
return _root;
}
}
private constructor(parent: NotificationTree<string, string> | null, name: Prev, childrenName: Curr[]) {
this.parent = parent;
this.name = name;
this.expandChildren(childrenName);
this.listener = new Map();
}
/**
* append children with name list
* @param childrenName
*/
private expandChildren(childrenName: Curr[]) {
const children: typeof this.children = {} as any;
for (const name of childrenName) {
if (children[name] === void 0) {
children[name] = new NotificationTree(this as any, name, []);
} else {
console.warn(`duplicate node name: ${name}`);
}
}
this.children = children;
}
get value() {
return this._value;
}
/**
* use private decorations to prevent external calls to value setters
*/
private set value(newVal: number) {
const delta = newVal - this._value;
if (this.parent) {
this.parent!.value += delta;
}
this._value = newVal;
try {
for (const [callback, { target, once }] of this.listener.entries()) {
callback.call(target, newVal);
if (once) {
this.unListen(callback);
}
}
} catch (e) {
// use try-catch to prevent break the setter chain
console.error(e);
}
}
/**
* append children to this node with specify name list
* @param which
* @param names
* @returns
*/
expand<Name extends string, WhichOne extends Curr>(which: WhichOne, names: Name[]): NotificationTree<JoinType<Prev, WhichOne>, Name> {
this.children[which].expandChildren(names);
return this.children[which];
}
/**
* set value, it will trigger ancestors' changed their value.
* make sure it's leaf node otherwise value will out of sync.
* @param value
*/
setValue(value: number) {
if (Object.keys(this.children).length) {
console.warn(`this node has children, set it's value can't keep the values consistent`)
}
this.value = value;
}
/**
* get children by name.
* make children field private in order to prevent children modified external.
* to prevent
* @param childName
* @returns
*/
getChild<N extends Curr, C extends string>(childName: N): Omit<NotificationTree<JoinType<Prev, N>, C>, 'getChild'> {
if (childName in this.children) {
return this.children[childName]
} else {
throw new Error(`${childName} is not [${this.name}]'s child`);
}
}
/**
* subscribe value changed event
* @param callback
* @param options target: context, once: cancel once triggered
* @returns a handler to unsubscribe event
*/
listen(callback: Callback, options: {
target?: any,
once: boolean,
} = { target: null, once: false }) {
this.listener.set(callback, { target: options.target, once: options.once });
return { cancel: () => this.unListen(callback) }
}
/**
* unsubscribe value changed event
* @param callback
*/
unListen(callback: Callback) {
this.listener.delete(callback);
}
/**
* dump value to console to show value intuitively in console
*/
dump() {
console.groupCollapsed(`${this.name} -> ${this.value}`);
for (const key in this.children) {
const child = this.children[key];
if (Object.keys(child.children).length) {
child.dump();
} else {
console.log(`${child.name} -> ${child.value}`)
}
}
console.groupEnd();
}
}
Reference
이 문제에 관하여(TypeScript에서 강력한 유형 프롬프트로 알림 트리 만들기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/oloshe/create-a-notification-tree-with-strong-type-prompt-in-typescript-i3f텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)