My goal is to ensure proper typing for an attributes
object that is stored in a wrapper class. This is necessary to maintain correct typing when accessing or setting individual attributes using the getOne
/setOne
methods as shown below.
However, I am facing an issue where strings are being accepted as parameters for the AttributeCollection
constructor, even though this goes against the intended purpose of the class:
type AttributeMap<M extends object> = {
[key in keyof M]: M[key];
};
export class AttributeCollection<M extends object> {
constructor(private attributes: AttributeMap<M>) {}
public getOne<K extends keyof AttributeMap<M>>(key: K): M[K] {
return this.attributes[key];
}
public setOne<K extends keyof AttributeMap<M>>(key: K, value: M[K]): void {
this.attributes[key] = value;
}
}
const acCorrectImplicit = new AttributeCollection({ a: 'str'}); // ok
const acCorrectExplicit = new AttributeCollection<{ a: string }>({ a: 'str'}); // ok
const acWrong = new AttributeCollection('str'); // ok, but shouldn't be
/*
The obtained typing:
const acWrong: AttributeCollection<{
toString: () => string;
charAt: (pos: number) => string;
charCodeAt: (index: number) => number;
concat: (...strings: string[]) => string;
indexOf: (searchString: string, position?: number | undefined) => number;
... 37 more ...;
[Symbol.iterator]: () => IterableIterator<...>;
}>
*/
const am: AttributeMap<'str'> = 'str'; // error
console.log(acCorrectImplicit, acCorrectExplicit, acWrong, am);
I suspect there might be some incorrect type passing occurring somewhere - can you assist me in identifying what's causing this issue?
Check out the playground.