how i can omit getters,setters,methods in Partial<>
data ?
Or existe a way to get this kind of architecture with typescript ?
I need a types with full access, and a types without readOnly.
I try make a small example here, see note://
export const $A = (() => {
class A {
_uid?: string = 'generateUID()';
_title?: string = '';
children?: A[] = [];
B?: InstanceType<typeof $B['B']> = $B.create();
get Title() {
return this._title || this._uid;
}
constructor(data: Partial<A> = {}) {
Object.assign(this, data);
}
getFullTitle() {
return this._title + this._uid;
}
}
function create(data?: Partial<A>) {
return new A(data);
}
function renderer(A: A) {
//#1 Allow full access public A in this renderer entity
const { Title, _title, _uid } = A; // acces all props autorized here
const fulltitle = A.getFullTitle(); // acces methods autorized here
const childTitle = A.children[0]?.Title; // acces child props autorized here
}
return {
create,
renderer,
A,
};
})();
export const $B = (() => {
class B {
_foo?: string = '';
get Foo() {
return this._foo + 'foo';
}
constructor(data: Partial<B> = {}) {
Object.assign(this, data);
}
}
function create(data?: Partial<B>) {
return new B(data);
}
function renderer(B: B) {}
return {
create,
renderer,
B,
};
})();
// API
//#2 Partial props only (omit methods,getter,setter) for a higth level API !
$A.create({ Title: '' }); // should not visible and lint error !
$A.create({ getFullTitle: () => '' }); // should not visible and lint error !
// want allow only this structure for higth API
$A.create({
_title: '',
_uid: '',
B: $B.create(),
children: [$A.create({ _title: '', _uid: '' }), $A.create({ _title: '', _uid: '' })],
});
What i want is inside function renderer(A: A) {
able to use all public props methodes,getter,setters.
And inside function create(data?: Partial<A>)
allow only partial datas (without getters,setters,methodes).
So i want a trick to for get only yellow props here, when i use create()
I try many scenario without success, Always hit a wall somewhere ! if you have a another's way to structure this ? thanks
To my knowledge, you cannot detect getters or setters in the type system; see microsoft/TypeScript#10196 for more information. It is said there that
Interfaces make no distinction between regular properties and accessor properties--it's an implementation detail left up to the implementer of the interface.
If you want to detect properties whose values are function-typed, you can use KeysMatching
from the answer to this question:
type KeysMatching<T, V> = { [K in keyof T]-?: T[K] extends V ? K : never }[keyof T];
type MethodKeysOfA = KeysMatching<A, Function>;
// type MethodKeysOfA = "getFullTitle"
If you want to detect properties marked as readonly
(and getter-only properties count), you can do so with ReadonlyKeys
from the answer to this question:
type IfEquals<X, Y, A = X, B = never> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? A : B;
type ReadonlyKeys<T> = {
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, never, P>
type ReadonlyKeysOfA = ReadonlyKeys<A>
// type ReadonlyKeysOfA = "Title"
You could then combine these to produce your "partial" A
:
type MyPartialA = Omit<A, MethodKeysOfA | ReadonlyKeysOfA>
/* type MyPartialA = {
_uid?: string | undefined;
_title?: string | undefined;
children?: A[] | undefined;
B?: B | undefined;
} */
If you use MyPartialA
instead of Partial<A>
for the type of the data
parameter in create()
, you get the behavior you're looking for:
$A.create({ Title: '' }); // error!
$A.create({ getFullTitle: () => '' }); // error!
$A.create({
_title: '',
_uid: '',
B: $B.create(),
children: [$A.create({ _title: '', _uid: '' }), $A.create({ _title: '', _uid: '' })],
}); // okay
It's also possible that you want to think of MyPartialA
as the set of optional properties from A
. That is, instead of excluding methods and readonly properties, you include things which might not be present in A
. If so, you could use the OptionalKeys
type from the answer to the same question with ReadonlyKeys
:
type OptionalKeys<T> = { [K in keyof T]-?:
({} extends { [P in K]: T[K] } ? K : never)
}[keyof T];
type OptionalKeysOfA = OptionalKeys<A>;
// type OptionalKeysOfA = "_uid" | "_title" | "children" | "B"
You can see that this leads to the same MyPartialA
as before:
type MyPartialA = Pick<A, OptionalKeysOfA>;
/* type MyPartialA = {
_uid?: string | undefined;
_title?: string | undefined;
children?: A[] | undefined;
B?: B | undefined;
} */