My class wraps an iterable object and implements iteration as:
[Symbol.iterator]() { return this.range[Symbol.iterator](); }
The compiler does not enforce the implements Iterable<T> info - why is that?
class SomeWrapper /* implements Iterable<number> */ {
constructor(public readonly range: MyRange) {}
[Symbol.iterator]() { return this.range[Symbol.iterator](); }
}
class MyRange {
constructor(
public readonly begin: number,
public readonly end: number
) {}
[Symbol.iterator]() {
return new MyRangeIterator(this);
}
}
class MyRangeIterator implements Iterator<number>
{
public index: number
public end: number
constructor(range: MyRange)
{
this.index = range.begin;
this.end = range.end
}
public next(): IteratorResult<number, "no_more_values">
{
if (this.index < this.end) {
return { done: false, value: this.index++ }
}
return { done: true, value: "no_more_values" }
}
}
usage:
const range = new MyRange(5, 14);
const wrapper = new SomeWrapper(range)
for (const x of wrapper) { // I expected an error here: SomeWrapper - Not Iterable
console.log(x)
}
TypeScript doesn't check if your class implements Iterable but checks if your class has Symbol.iterator property.
It will no longer typecheck when you remove this property or make this property not to return an iterator.
Iterators and Generators explains it.
An object is deemed iterable if it has an implementation for the Symbol.iterator property.
TypeScript typechecks in this way in general as it's based on structural subtyping.
For example, this typechecks even though Y doesn't implements X.
interface X {
readonly x: string;
}
class Y {
readonly x: string = 'y';
}
function f(x: X): void {}
f(new Y());
Related
I need to create a dictionary object in typescript, something that has the following function:
obj.add(key, value);
obj.remove(key);
obj.contains(key); // returns true/false
obj.get(key); // returns value
This was the model I created to accommodate:
export class Dictionary<T, F> {
items = {};
constructor() {
this.items = {};
}
public contains(key: T): boolean {
return key in this.items;
}
public add(key: T, value: F): void {
this.items[key] = value;
}
public get(key: T): F {
return this.items[key];
}
public remove(key: T): boolean {
if (this.contains(key) ){
delete this.items[key];
return true;
}
return false;
}
}
However, I keep getting this error:
Error: src/app/models/dictionary.model.ts:10:7 - error TS2536: Type 'T' cannot be used to index type '{}'.
10 this.items[key] = value;
Has anyone come across this before and can recommend the most appropriate fix?
Thanks in advance for any pointers (examples welcome :-))!
My problem:
I need to differentiate between the private, public and getter (get X()) properties of a typescript class.
My Project:
I have an Angular project, that has a model design pattern. Aka. an user model would look like this
class UserModel extends BaseModel {
private _id: number;
get id() { return this._id; }
set id( _id: number ) { this._id = _id; }
}
To send these models to the backend, I just JSON.stringify() them, which if the user id is set as 13, returns an object like this
{
_id: 13
}
Now I need to modify the toJSON() function on the UserModel, so that instead of returning the private properties of the object, I will return the get X() variables only. The output should look like this.
{
id: 13
}
I made this simple function, to retrieve all properties of an object, but this gives me the private properties and the get properties both.
abstract class BaseModel {
public propsToObj() : {[key: string]: any} {
let ret: any = {};
for (var prop in this) {
ret[prop] = this[prop];
}
return ret;
}
}
and the toJSON function looks like this
class UserModel extends BaseModel {
private _id: number;
get id() { return this._id; }
set id( _id: number ) { this._id = _id; }
toJSON() {
return this.propsToObj();
}
}
The outcome of stringify-ing the UserModel looks like this
{
_id: 13,
id: 13
}
In conclusion, I need to know the visibility and type (getter or regular variable?) of properties on a class, how would I achieve this?
your propsToObj() is working wrong, it gets just all properties, you need to change it so it will get only getters, for example you can use this
abstract class BaseModel {
public propsToObj() : {[key: string]: any} {
let ret: any = {};
for (const prop in this) {
const descriptor = Object.getOwnPropertyDescriptor(this.constructor.prototype, prop);
if (descriptor && typeof descriptor.get === 'function') {
ret[prop] = this[prop];
}
}
return ret;
}
}
Object.getOwnPropertyDescriptor will get descriptor of a property and from which you can check if there is get function in descriptor, if it is then your property is getter, if not it is regular property, you can read more about descriptors here MDN(descriptors)
The last question you asked
I need to know the visibility and type of properties on a class, how
would I achieve this?
As I know you can't get the visibility of a property, as for the type if you want to know data type of a property you can use typeof for it.
EXAMPLE in propsToObj() method:
public propsToObj() : {[key: string]: any} {
let ret: any = {};
for (const prop in this) {
const descriptor = Object.getOwnPropertyDescriptor(this.constructor.prototype, prop);
if (descriptor && typeof descriptor.get === 'function') {
ret[prop] = this[prop];
console.log(typeof ret[prop]); // just exaple how you can know type you can save it with property too if you need it
}
}
return ret;
}
This is a typescript class with an exposed but not editable attribute:
export class Category {
private _id: number;
constructor(id: number){
this._id = id;
}
public get id(){
return this._id;
}
}
I would like to map it from a JSON like this:
{ id: 1 }
There are some obvious problems here:
I know that "_id" can't be magically mapped to "id" so maybe I could implement a custom logic that renames all the attributes with a _ before the name
I would like to keep the constructor with the id param but maybe I'm not able to map an object who require arguments before instantiation
With an empty constructor, I tried Object.assign(new Category(), jsonObject), however, this does not work since Cannot set property id of #<Category> which has only a getter
What I want to avoid is to write custom mapping logic for every class like this with private attributes, I tried some other solutions and libraries but didn't work for my situation, they're all referencing to class with only public attributes
I don't even know if what I ask is achievable, so if the case it isn't, then I will "surrender" to use the class with only public attributes
The missconception here is that you need a getter/setter at all just to manage visibility. You can't prevent code from accessing and modifying id, no matter what you do. You can however tell the IDE (and the developer using it) that he can only read/get the property by using the readonly modifier, which simplifies your code to:
export class Category {
readonly id: number;
}
Noe that readonly thing only exists at compile time, and doesnt have any affects at runtime. Your IDE will prevent you from doing:
(new Category).id = 5;
But it allows you to easily do:
const category = Object.assign(new Category, { id: 5 });
Pass the object through constructor:
export class Category {
private _a: number;
private _b: string;
constructor(values: { a: number; b: string }){
this._a = values.a;
this._b = values.b;
}
public getA() { return this._a; }
public getB() { return this._b; }
}
You can still call it with or without JSON:
let cat1 = Category (values: { a: 42, b: 'hello' });
let json = '{"a":42,"b":"hello"}'
let cat2 = Category (values: JSON.parse (json));
Alternatively, keep the values in an object rather than a direct member. This makes it unnecessary to map the object to member variables:
export class Category {
private _values: {
a: number;
b: string;
}
constructor(values: { a: number; b: string }){
this._values = values;
}
public getA() { return this._values.a; }
public getB() { return this._values.b; }
}
If you want to keep the old constructor, you can do so like this:
export class Category {
private _values: {
a: number;
b: string;
}
constructor(a: number, b: string) {
this._values = { a: a, b: b };
}
public static make (values: { a: number; b: string }) {
this._values = values;
}
public getA() { return this._values.a; }
public getB() { return this._values.b; }
}
I'm a new game developer who started from C#.
Now I need to transfer one of my games to typescript.
I tried to customize a list in typescript which i'm very familiar to in C#.
my code is like below:
export class List {
private items: Array;
constructor() {
this.items = [];
}
get count(): number {
return this.items.length;
}
add(value: T): void {
this.items.push(value);
}
get(index: number): T {
return this.items[index];
}
contains(item: T): boolean{
if(this.items.indexOf(item) != -1){
return true;
}else{
return false;
}
}
clear(){
this.items = [];
}
}
Still, I want to make like a array so I can do things like:
someList[i] = this.items[i];
I guess it's something like operator overload but I'm not quite sure.
Can any one tell me how to make it?
Thanks in advance.
Simply extend Array
export class List<T> extends Array<T> {
constructor() {
super();
}
get count(): number {
return this.length;
}
add(value: T): void {
this.push(value);
}
get(index: number): T {
return this[index];
}
contains(item: T): boolean {
if (this.indexOf(item) != -1) {
return true;
} else {
return false;
}
}
clear() {
this.splice(0, this.count);
}
}
To achieve the effect of overloading the indexing operator, you would have to use a proxy to get the runtime behavior and then use an index signature for the typing, like this:
interface List<T> {
[n: number]: T;
}
But proxies are serious machinery. You might be better off living with the boilerplate of method calls.
I have a class where I want a simple factory method:
class GTree{
public static createNode(){
return new GNode();
}
}
This means that I don't want to allow the consumer to immediately instantiate the GNode.
How do I properly implement this?
Obviously I can't do:
class GNode{
constructor(){
throw TypeError("This is nonsense");
}
}
Because then I can't create nodes anymore at all.
How do I force using the factory?
Here's a simpler scheme than my earlier comments. Just define the GNode class in a private (but shared) scope and thus that's the only place the constructor can be called from and also reset the .constructor property so it doesn't leak out:
const GTree = (function() {
class GNode {
constructor() {
}
someOtherMethod() {
console.log("someOtherMethod");
}
}
// reset public .constructor
GNode.prototype.constructor = function() {
throw new Error("Can't call GNode constructor directly");
};
class GTree {
constructor() {
this.nodes = [];
}
createNode() {
let node = new GNode();
this.nodes.push(node);
return node;
}
get length() {
return this.nodes.length;
}
}
return GTree;
})();
let tree = new GTree();
let node1 = tree.createNode();
let node2 = tree.createNode();
node1.someOtherMethod();
console.log(tree.length + " nodes");
You can't really do that in javascript, but you can do this:
export class GTree {
public static createNode(name: string): GNode {
return new GNodeImpl(name);
}
}
export interface GNode {
name: string;
}
class GNodeImpl implements GNode {
constructor(public name: string) {}
}
(code in playground)
Only GTree and the GNode interface are exported, meaning that it's not possible to instantiate GNodeImpl from outside the module.
I added the name property just for the example.