Extend native JavaScript array - javascript

Is there any way to inherit a class from JS native function?
For example, I have a JS function like this:
function Xarray()
{
Array.apply(this, arguments);
//some stuff for insert, add and remove notification
}
Xarray.prototype = new Array();
I tried to convert it to Typescript but i failed!!
export class Xarray implements Array {
}
The compiler asks me to define all Array interface properties. I know if I need this Xarray.prototype = new Array();, I have to extend Array in TS.
How to extend the JS native object in TS?

Starting in TypeScript 1.6, you can extend the Array type, see What's new in TypeScript
Here's an example:
class MyNewArray<T> extends Array<T> {
getFirst() {
return this[0];
}
}
var myArray = new MyNewArray<string>();
myArray.push("First Element");
console.log(myArray.getFirst()); // "First Element"
If you are emitting to ES5 or below, then use the following code:
class MyNewArray<T> extends Array<T> {
constructor(...items: T[]) {
super(...items);
Object.setPrototypeOf(this, MyNewArray.prototype);
}
getFirst() {
return this[0];
}
}
Read more about why this is necessary here.

I don't think there is a way to inherit existing interfaces like Array,
export class Xarray implements Array {
}
You should create a function and inherit it with its prototype. Typescript also will accept it which is similar to javascript.
function Xarray(...args: any[]): void; // required in TS 0.9.5
function Xarray()
{
Array.apply(this, arguments);
// some stuff for insert, add and remove notification
}
Xarray.prototype = new Array();
UPDATE: This one is discussed well and provided the best solution for this at jqfaq.com.
//a dummy class it to inherite array.
class XArray {
constructor() {
Array.apply(this, arguments);
return new Array();
}
// we need this, or TS will show an error,
//XArray["prototype"] = new Array(); will replace with native js arrray function
pop(): any { return "" };
push(val): number { return 0; };
length: number;
}
//Adding Arrray to XArray prototype chain.
XArray["prototype"] = new Array();
//our Class
class YArray extends XArray {
///Some stuff
}
var arr = new YArray();
//we can use the array prop here.
arr.push("one");
arr.push("two");
document.writeln("First Elemet in array : " + arr[0]);
document.writeln("</br>Array Lenght : " + arr.length);
Hope, this might help you!!!

Yes it's possible to extend a native JS object in TS, however there is an issue extending built-in types (those included in lib.d.ts) like Array. Read this post for workaround: http://typescript.codeplex.com/workitem/4
So defining a type interface which extends a native type object at a later stage can be done in the following way:
/// <reference path="lib.d.ts"/>
interface Array {
sort: (input: Array) => Array;
}
Using on a concrete example, you can sort some elements on an array which define a sort function in an interface and later implements it on an object.
class Math implements Array {
sort : (x: Array) => Array {
// sorting the array
}
}
var x = new Math();
x.sort([2,3,32,3]);

While researching this, I came across Ben Nadel's excellent post on Extending JavaScript Arrays While Keeping Native Bracket-Notation Functionality. After some initial confusion on how to succesfully convert this into TypeScript, I created a fully working Collection class that can be subclassed.
It can do everything an Array can, including indexing by brackets,use in loop constructions (for, while, forEach), maps, etc.
The main implementation points are
Create an array in the constructor, add the methods to the array and return that from the constructor
Copy dummy declarations of Array methods to pass the implements Array bit
Example of usage:
var foo = new Foo({id : 1})
var c = new Collection();
c.add(foo)
c.length === 1; // => true
foo === c[0]; // => true
foo === c.find(1); // => true
I made it available as a gist, complete with tests and an example implementation of a subclass, but I present the full source here:
/*
* Utility "class" extending Array with lookup functions
*
* Typescript conversion of Ben Nadel's Collection class.
* https://gist.github.com/fatso83/3773d4cb5f39128b3732
*
* #author Carl-Erik Kopseng
* #author Ben Nadel (javascript original)
*/
export interface Identifiable {
getId : () => any;
}
export class Collection<T extends Identifiable> implements Array<T> {
constructor(...initialItems:any[]) {
var collection = Object.create(Array.prototype);
Collection.init(collection, initialItems, Collection.prototype);
return collection;
}
static init(collection, initialItems:any[], prototype) {
Object.getOwnPropertyNames(prototype)
.forEach((prop) => {
if (prop === 'constructor') return;
Object.defineProperty(collection, prop, { value: prototype[prop] })
});
// If we don't redefine the property, the length property is suddenly enumerable!
// Failing to do this, this would fail: Object.keys([]) === Object.keys(new Collection() )
Object.defineProperty(collection, 'length', {
value: collection.length,
writable: true,
enumerable: false
});
var itemsToPush = initialItems;
if (Array.isArray(initialItems[0]) && initialItems.length === 1) {
itemsToPush = initialItems[0];
}
Array.prototype.push.apply(collection, itemsToPush);
return collection;
}
// Find an element by checking each element's getId() method
public find(id:any):T;
// Find an element using a lookup function that
// returns true when given the right element
public find(lookupFn:(e:T) => boolean):T ;
find(x:any) {
var res, comparitor;
if (typeof x === 'function') {
comparitor = x;
} else {
comparitor = (e) => {
return e.getId() === x;
}
}
res = [].filter.call(this, comparitor);
if (res.length) return res[0];
else return null;
}
// Add an element
add(value:T);
// Adds all ements in the array (flattens it)
add(arr:T[]);
add(arr:Collection<T>);
add(value) {
// Check to see if the item is an array or a subtype thereof
if (value instanceof Array) {
// Add each sub-item using default push() method.
Array.prototype.push.apply(this, value);
} else {
// Use the default push() method.
Array.prototype.push.call(this, value);
}
// Return this object reference for method chaining.
return this;
}
remove(elem:T):boolean;
remove(lookupFn:(e:T) => boolean):boolean ;
remove(x:any):boolean {
return !!this._remove(x);
}
/**
* #return the removed element if found, else null
*/
_remove(x:any):T {
var arr = this;
var index = -1;
if (typeof x === 'function') {
for (var i = 0, len = arr.length; i < len; i++) {
if (x(this[i])) {
index = i;
break;
}
}
} else {
index = arr.indexOf(x);
}
if (index === -1) {
return null;
}
else {
var res = arr.splice(index, 1);
return res.length ? res[0] : null;
}
}
// dummy declarations
// "massaged" the Array interface definitions in lib.d.ts to fit here
toString:()=> string;
toLocaleString:()=> string;
concat:<U extends T[]>(...items:U[])=> T[];
join:(separator?:string)=> string;
pop:()=> T;
push:(...items:T[])=> number;
reverse:()=> T[];
shift:()=> T;
slice:(start?:number, end?:number)=> T[];
sort:(compareFn?:(a:T, b:T) => number)=> T[];
splice:(start?:number, deleteCount?:number, ...items:T[])=> T[];
unshift:(...items:T[])=> number;
indexOf:(searchElement:T, fromIndex?:number)=> number;
lastIndexOf:(searchElement:T, fromIndex?:number)=> number;
every:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> boolean;
some:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> boolean;
forEach:(callbackfn:(value:T, index:number, array:T[]) => void, thisArg?:any)=> void;
map:<U>(callbackfn:(value:T, index:number, array:T[]) => U, thisArg?:any)=> U[];
filter:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> T[];
reduce:<U>(callbackfn:(previousValue:U, currentValue:T, currentIndex:number, array:T[]) => U, initialValue:U)=> U;
reduceRight:<U>(callbackfn:(previousValue:U, currentValue:T, currentIndex:number, array:T[]) => U, initialValue:U)=> U;
length:number;
[n: number]: T;
}
Of course, the bits on Identifiable, the find and remove methods are not needed, but I supply them none the less as a full fledged example is a tad more usable than a bare-bones Collection without any methods of its own.

Constructors that return an object implicitly substitute the value of this for callers of super(). Generated constructor code has to capture whatever super() returns and replace it with this.
Built-in classes use ES6 new.target to do the fixup but there's no way for ES5 code to ensure that new.target has a value calling the constructor.
This is why your extra methods vanish - your object has the wrong prototype.
All you need to do is fix the prototype chain after calling super().
export class RoleSet extends Array {
constructor() {
super();
Object.setPrototypeOf(this, RoleSet.prototype);
}
private containsRoleset(roleset:RoleSet){
if (this.length < roleset.length) return false;
for (var i = 0; i < roleset.length; i++) {
if (this.indexOf(roleset[i]) === -1) return false;
}
return true;
}
public contains(item: string | RoleSet): boolean {
if (item) {
return typeof item === "string" ?
this.indexOf(item) !== -1 :
this.containsRoleset(item);
} else {
return true;
}
}
}
Be aware that this curse shall afflict thy children and thy children's children until the end of code; you have to do the fixup in every generation of an inheritance chain.

In your case, a good bet would be to use this pattern:
function XArray(array) {
array = array || [];
//add a new method
array.second = function second() {
return array[1];
};
//overwrite an existing method with a super type pattern
var _push = array.push;
array.push = function push() {
_push.apply(array, arguments);
console.log("pushed: ", arguments);
};
//The important line.
return array
}
Then you can do:
var list = XArray([3, 4]);
list.second() ; => 4
list[1] = 5;
list.second() ; => 5
note however that:
list.constructor ; => Array and not XArray

If you already have a working Xarray implementation, I don't see the point in recreating it in typescript, which eventually will compile back to JavaScript.
But I do see the point in being able to use the Xarray in TypeScript.
In order to accomplish this, you simply need an interface for your Xarray. You don't even need to have a concrete implementation of your interface since your existing js implementation will serve as one.
interface Xarray{
apply(...arguments : any[]) : void;
//some stuff for insert, add and ...
}
declare var Xarray: {
new (...items: any[]): Xarray;
(...items: any[]): Xarray;
prototype: Array; // This should expose all the Array stuff from ECMAScript
}
After doing this, should be able to use your custom defined type through the declared variable without actually implementing it in TypeScript.
var xArr = new Xarray();
xArr.apply("blah", "hehe", "LOL");
You might look for reference here to see how they typed the ECMAScript Array API:
http://typescript.codeplex.com/SourceControl/changeset/view/2bee84410e02#bin/lib.d.ts

Yes you can augment the Builtin types and do it in a way that doesn't require all the paraphernalia of an XArray as described in the other answers and is closer to how you would do it in javascript.
Typescript allows a number of ways to do this, but for the Builtin types like Array and Number you need to use "merging" and declare the global namespace to augment the types, see the docs
so for Array we can add an optional metadata object and a get first member
declare global {
interface Array<T> {
meta?: any|null ,
getFirst(): T
}
}
if(!Array.prototype.meta )
{
Array.prototype.meta = null
}
if(!Array.prototype.getFirst )
{
Array.prototype.getFirst = function() {
return this[0];
}
}
we can use this like so:
let myarray: number[] = [ 1,2,3 ]
myarray.meta = { desc: "first three ints" }
let first: number = myarray.getFirst()
The same goes for Number say I want to add a modulo function that isn't limited like the remainder %
declare global {
interface Number {
mod(n:number): number
}
}
if(!Number.prototype.mod )
{
Number.prototype.mod = function (n: number) {
return ((this % n) + n) % n;
}
}
and we can use it like so:
let foo = 9;
console.log("-9.mod(5) is "+ foo.mod(5))
For Functions that we may want to add an API to ie to make it behave like a function and an object we can use hybrid types (see docs)
// augment a (number) => string function with an API
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
//helper function to get my augmented counter function with preset values
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
use it like so:-
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

With purpose to overcome the problem of extension of the native Array class, I took advantage of a decorator.
function extendArray(constructor: Function) {
Object.getOwnPropertyNames(constructor.prototype)
.filter(name => name !== 'constructor')
.forEach(name => {
const attributes = Object.getOwnPropertyDescriptor(constructor.prototype, name);
Object.defineProperty(Array.prototype, name, attributes);
});
}
#extendArray
export class Collection<T> extends Array<T> {
constructor(...args: T[]) {
super(...args);
}
// my appended methods
}
BTW This decorator can be made more generic (for other native classes) if to use a decorator factory.

Don't know how frowned upon this is but for example I needed to create an array of BulletTypes so that I could cycle through them. What I did is the following:
interface BulletTypesArray extends Array<BulletType> {
DefaultBullet?: Bullet;
}
var BulletTypes: BulletTypesArray = [ GreenBullet, RedBullet ];
BulletTypes.DefaultBullet = GreenBullet;
Obviously you could could also make a generic interface, something like interface SuperArray<T> extends Array<T>.

Related

How to check the interface type of an object? [duplicate]

This question is the direct analogon to Class type check with TypeScript
I need to find out at runtime if a variable of type any implements an interface. Here's my code:
interface A{
member:string;
}
var a:any={member:"foobar"};
if(a instanceof A) alert(a.member);
If you enter this code in the typescript playground, the last line will be marked as an error, "The name A does not exist in the current scope". But that isn't true, the name does exist in the current scope. I can even change the variable declaration to var a:A={member:"foobar"}; without complaints from the editor. After browsing the web and finding the other question on SO I changed the interface to a class but then I can't use object literals to create instances.
I wondered how the type A could vanish like that but a look at the generated javascript explains the problem:
var a = {
member: "foobar"
};
if(a instanceof A) {
alert(a.member);
}
There is no representation of A as an interface, therefore no runtime type checks are possible.
I understand that javascript as a dynamic language has no concept of interfaces. Is there any way to type check for interfaces?
The typescript playground's autocompletion reveals that typescript even offers a method implements. How can I use it ?
You can achieve what you want without the instanceof keyword as you can write custom type guards now:
interface A {
member: string;
}
function instanceOfA(object: any): object is A {
return 'member' in object;
}
var a: any = {member: "foobar"};
if (instanceOfA(a)) {
alert(a.member);
}
Lots of Members
If you need to check a lot of members to determine whether an object matches your type, you could instead add a discriminator. The below is the most basic example, and requires you to manage your own discriminators... you'd need to get deeper into the patterns to ensure you avoid duplicate discriminators.
interface A {
discriminator: 'I-AM-A';
member: string;
}
function instanceOfA(object: any): object is A {
return object.discriminator === 'I-AM-A';
}
var a: any = {discriminator: 'I-AM-A', member: "foobar"};
if (instanceOfA(a)) {
alert(a.member);
}
In TypeScript 1.6, user-defined type guard will do the job.
interface Foo {
fooProperty: string;
}
interface Bar {
barProperty: string;
}
function isFoo(object: any): object is Foo {
return 'fooProperty' in object;
}
let object: Foo | Bar;
if (isFoo(object)) {
// `object` has type `Foo`.
object.fooProperty;
} else {
// `object` has type `Bar`.
object.barProperty;
}
And just as Joe Yang mentioned: since TypeScript 2.0, you can even take the advantage of tagged union type.
interface Foo {
type: 'foo';
fooProperty: string;
}
interface Bar {
type: 'bar';
barProperty: number;
}
let object: Foo | Bar;
// You will see errors if `strictNullChecks` is enabled.
if (object.type === 'foo') {
// object has type `Foo`.
object.fooProperty;
} else {
// object has type `Bar`.
object.barProperty;
}
And it works with switch too.
How about User-Defined Type Guards? https://www.typescriptlang.org/docs/handbook/advanced-types.html
interface Bird {
fly();
layEggs();
}
interface Fish {
swim();
layEggs();
}
function isFish(pet: Fish | Bird): pet is Fish { //magic happens here
return (<Fish>pet).swim !== undefined;
}
// Both calls to 'swim' and 'fly' are now okay.
if (isFish(pet)) {
pet.swim();
}
else {
pet.fly();
}
typescript 2.0 introduce tagged union
Typescript 2.0 features
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
function area(s: Shape) {
// In the following switch statement, the type of s is narrowed in each case clause
// according to the value of the discriminant property, thus allowing the other properties
// of that variant to be accessed without a type assertion.
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.width * s.height;
case "circle": return Math.PI * s.radius * s.radius;
}
}
It's now possible, I just released an enhanced version of the TypeScript compiler that provides full reflection capabilities. You can instantiate classes from their metadata objects, retrieve metadata from class constructors and inspect interface/classes at runtime. You can check it out here
Usage example:
In one of your typescript files, create an interface and a class that implements it like the following:
interface MyInterface {
doSomething(what: string): number;
}
class MyClass implements MyInterface {
counter = 0;
doSomething(what: string): number {
console.log('Doing ' + what);
return this.counter++;
}
}
now let's print some the list of implemented interfaces.
for (let classInterface of MyClass.getClass().implements) {
console.log('Implemented interface: ' + classInterface.name)
}
compile with reflec-ts and launch it:
$ node main.js
Implemented interface: MyInterface
Member name: counter - member kind: number
Member name: doSomething - member kind: function
See reflection.d.ts for Interface meta-type details.
UPDATE:
You can find a full working example here
Type guards in Typescript:
TS has type guards for this purpose. They define it in the following manner:
Some expression that performs a runtime check that guarantees the type
in some scope.
This basically means that the TS compiler can narrow down the type to a more specific type when it has sufficient information. For example:
function foo (arg: number | string) {
if (typeof arg === 'number') {
// fine, type number has toFixed method
arg.toFixed()
} else {
// Property 'toFixed' does not exist on type 'string'. Did you mean 'fixed'?
arg.toFixed()
// TSC can infer that the type is string because
// the possibility of type number is eliminated at the if statement
}
}
To come back to your question, we can also apply this concept of type guards to objects in order to determine their type. To define a type guard for objects, we need to define a function whose return type is a type predicate. For example:
interface Dog {
bark: () => void;
}
// The function isDog is a user defined type guard
// the return type: 'pet is Dog' is a type predicate,
// it determines whether the object is a Dog
function isDog(pet: object): pet is Dog {
return (pet as Dog).bark !== undefined;
}
const dog: any = {bark: () => {console.log('woof')}};
if (isDog(dog)) {
// TS now knows that objects within this if statement are always type Dog
// This is because the type guard isDog narrowed down the type to Dog
dog.bark();
}
Here's another option: the module ts-interface-builder provides a build-time tool that converts a TypeScript interface into a runtime descriptor, and ts-interface-checker can check if an object satisfies it.
For OP's example,
interface A {
member: string;
}
You'd first run ts-interface-builder which produces a new concise file with a descriptor, say, foo-ti.ts, which you can use like this:
import fooDesc from './foo-ti.ts';
import {createCheckers} from "ts-interface-checker";
const {A} = createCheckers(fooDesc);
A.check({member: "hello"}); // OK
A.check({member: 17}); // Fails with ".member is not a string"
You can create a one-liner type-guard function:
function isA(value: any): value is A { return A.test(value); }
I would like to point out that TypeScript does not provide a direct mechanism for dynamically testing whether an object implements a particular interface.
Instead, TypeScript code can use the JavaScript technique of checking whether an appropriate set of members are present on the object. For example:
var obj : any = new Foo();
if (obj.someInterfaceMethod) {
...
}
same as above where user-defined guards were used but this time with an arrow function predicate
interface A {
member:string;
}
const check = (p: any): p is A => p.hasOwnProperty('member');
var foo: any = { member: "foobar" };
if (check(foo))
alert(foo.member);
In my opinion this is the best approach; attach a "Fubber" symbol to the interfaces. It is MUCH faster to write, MUCH faster for the JavaScript engine than a type guard, supports inheritance for interfaces and makes type guards easy to write if you need them.
This is the purpose for which ES6 has symbols.
Interface
// Notice there is no naming conflict, because interfaces are a *type*
export const IAnimal = Symbol("IAnimal");
export interface IAnimal {
[IAnimal]: boolean; // the fubber
}
export const IDog = Symbol("IDog");
export interface IDog extends IAnimal {
[IDog]: boolean;
}
export const IHound = Symbol("IDog");
export interface IHound extends IDog {
// The fubber can also be typed as only 'true'; meaning it can't be disabled.
[IDog]: true;
[IHound]: boolean;
}
Class
import { IDog, IAnimal } from './interfaces';
class Dog implements IDog {
// Multiple fubbers to handle inheritance:
[IAnimal] = true;
[IDog] = true;
}
class Hound extends Dog implements IHound {
[IHound] = true;
}
Testing
This code can be put in a type guard if you want to help the TypeScript compiler.
import { IDog, IAnimal } from './interfaces';
let dog = new Dog();
if (dog instanceof Hound || dog[IHound]) {
// false
}
if (dog[IAnimal]?) {
// true
}
let houndDog = new Hound();
if (houndDog[IDog]) {
// true
}
if (dog[IDog]?) {
// it definitely is a dog
}
TypeGuards
interface MyInterfaced {
x: number
}
function isMyInterfaced(arg: any): arg is MyInterfaced {
return arg.x !== undefined;
}
if (isMyInterfaced(obj)) {
(obj as MyInterfaced ).x;
}
Approaching 9 years since OP, and this problem remains. I really REALLY want to love Typescript. And usually I succeed. But its loopholes in type safety is a foul odor that my pinched nose can't block.
My goto solutions aren't perfect. But my opinion is they are better than most of the more commonly prescribed solutions. Discriminators have proven to be a bad practice because they limit scalability and defeat the purpose of type safety altogether. My 2 prettiest butt-ugly solutions are, in order:
Class Decorator:
Recursively scans the typed object's members and computes a hash based on the symbol names. Associates the hash with the type name in a static KVP property. Include the type name in the hash calculation to mitigate risk of ambiguity with ancestors (happens with empty subclasses).
Pros: It's proven to be the most trustworthy. It is also provides very strict enforcements. This is also similar to how other high-level languages natively implement polymorphism. Howbeit, the solution requires much further extension in order to be truly polymorphic.
Cons: Anonymous/JSON objects have to be rehashed with every type check, since they have no type definitions to associate and statically cache. Excessive stack overhead results in significant performance bottlenecks in high load scenarios. Can be mitigated with IoC containers, but that can also be undesirable overhead for small apps with no other rationale. Also requires extra diligence to apply the decorator to every object requiring it.
Cloning:
Very ugly, but can be beneficial with thoughtful strategies. Create a new instance of the typed object and reflexively copy the top-level member assignments from the anonymous object. Given a predetermined standard for passage, you can simultaneously check and clone-cast to types. Something akin to "tryParse" from other languages.
Pros: In certain scenarios, resource overhead can be mitigated by immediately using the converted "test" instance. No additional diligence required for decorators. Large amount of flexibility tolerances.
Cons: Memory leaks like a flour sifter. Without a "deep" clone, mutated references can break other components not anticipating the breach of encapsulation. Static caching not applicable, so operations are executed on each and every call--objects with high quantities of top-level members will impact performance. Developers who are new to Typescript will mistake you for a junior due to not understanding why you've written this kind of pattern.
All totalled: I don't buy the "JS doesn't support it" excuse for Typescript's nuances in polymorphism. Transpilers are absolutely appropriate for that purpose. To treat the wounds with salt: it comes from Microsoft. They've solved this same problem many years ago with great success: .Net Framework offered a robust Interop API for adopting backwards compatibility with COM and ActiveX. They didn't try to transpile to the older runtimes. That solution would have been much easier and less messy for a loose and interpreted language like JS...yet they cowered out with the fear of losing ground to other supersets. Using the very shortcomings in JS that was meant to be solved by TS, as a malformed basis for redefining static typed Object-Oriented principle is--well--nonsense. It smacks against the volumes of industry-leading documentation and specifications which have informed high-level software development for decades.
Based on Fenton's answer, here's my implementation of a function to verify if a given object has the keys an interface has, both fully or partially.
Depending on your use case, you may also need to check the types of each of the interface's properties. The code below doesn't do that.
function implementsTKeys<T>(obj: any, keys: (keyof T)[]): obj is T {
if (!obj || !Array.isArray(keys)) {
return false;
}
const implementKeys = keys.reduce((impl, key) => impl && key in obj, true);
return implementKeys;
}
Example of usage:
interface A {
propOfA: string;
methodOfA: Function;
}
let objectA: any = { propOfA: '' };
// Check if objectA partially implements A
let implementsA = implementsTKeys<A>(objectA, ['propOfA']);
console.log(implementsA); // true
objectA.methodOfA = () => true;
// Check if objectA fully implements A
implementsA = implementsTKeys<A>(objectA, ['propOfA', 'methodOfA']);
console.log(implementsA); // true
objectA = {};
// Check again if objectA fully implements A
implementsA = implementsTKeys<A>(objectA, ['propOfA', 'methodOfA']);
console.log(implementsA); // false, as objectA now is an empty object
I found an example from #progress/kendo-data-query in file filter-descriptor.interface.d.ts
Checker
declare const isCompositeFilterDescriptor: (source: FilterDescriptor | CompositeFilterDescriptor) => source is CompositeFilterDescriptor;
Example usage
const filters: Array<FilterDescriptor | CompositeFilterDescriptor> = filter.filters;
filters.forEach((element: FilterDescriptor | CompositeFilterDescriptor) => {
if (isCompositeFilterDescriptor(element)) {
// element type is CompositeFilterDescriptor
} else {
// element type is FilterDescriptor
}
});
You can validate a TypeScript type at runtime using ts-validate-type, like so (does require a Babel plugin though):
const user = validateType<{ name: string }>(data);
I knew I'd stumbled across a github package that addressed this properly, and after trawling through my search history I finally found it. Check out typescript-is - though it requires your code to be compiled using ttypescript (I am currently in the process of bullying it into working with create-react-app, will update on the success/failure later), you can do all sorts of crazy things with it. The package is also actively maintained, unlike ts-validate-type.
You can check if something is a string or number and use it as such, without the compiler complaining:
import { is } from 'typescript-is';
const wildString: any = 'a string, but nobody knows at compile time, because it is cast to `any`';
if (is<string>(wildString)) { // returns true
// wildString can be used as string!
} else {
// never gets to this branch
}
if (is<number>(wildString)) { // returns false
// never gets to this branch
} else {
// Now you know that wildString is not a number!
}
You can also check your own interfaces:
import { is } from 'typescript-is';
interface MyInterface {
someObject: string;
without: string;
}
const foreignObject: any = { someObject: 'obtained from the wild', without: 'type safety' };
if (is<MyInterface>(foreignObject)) { // returns true
const someObject = foreignObject.someObject; // type: string
const without = foreignObject.without; // type: string
}
Type guards in Typescript using Reflect
Here is an example of a type guard from my Typescript game engine
export interface Start {
/**
* Start is called on the frame when a script is enabled just before any of the Update methods are called the first time.
*/
start(): void
}
/**
* User Defined Type Guard for Start
*/
export const implementsStart = (arg: any): arg is Start => {
return Reflect.has(arg, 'start')
}
/**
* Example usage of the type guard
*/
start() {
this.components.forEach(component => {
if (implementsStart(component)) {
component.start()
}
})
}
export interface ConfSteps {
group: string;
key: string;
steps: string[];
}
private verify(): void {
const obj = `{
"group": "group",
"key": "key",
"steps": [],
"stepsPlus": []
} `;
if (this.implementsObject<ConfSteps>(obj, ['group', 'key', 'steps'])) {
console.log(`Implements ConfSteps: ${obj}`);
}
}
private objProperties: Array<string> = [];
private implementsObject<T>(obj: any, keys: (keyof T)[]): boolean {
JSON.parse(JSON.stringify(obj), (key, value) => {
this.objProperties.push(key);
});
for (const key of keys) {
if (!this.objProperties.includes(key.toString())) {
return false;
}
}
this.objProperties = null;
return true;
}
Another solution could be something similar what is used in case of HTMLIFrameElement interface. We can declare a variable with the same name by creating an object by the interface if we know that there is an implementation for it in another module.
declare var HTMLIFrameElement: {
prototype: HTMLIFrameElement;
new(): HTMLIFrameElement;
};
So in this situation
interface A {
member:string;
}
declare var A : {
prototype: A;
new(): A;
};
if(a instanceof A) alert(a.member);
should work fine
This answer is very simple. However, this solution is at least possible (though not always ideal) in maybe 3/4 of the cases. So, in other words, this is probably relevant to whomever is reading this.
Let's say I have a very simple function that needs to know a parameter's interface type:
const simpleFunction = (canBeTwoInterfaces: interfaceA | interface B) => {
// if interfaceA, then return canBeTwoInterfaces.A
// if interfaceB, then return canBeTwoInterfaces.B
}
The answers that are getting the most upvotes tend to be using "function checking". i.e.,
const simpleFunction = (canBeTwoInterfaces: interfaceA | interface B) => {
if (canBeTwoInterfaces.onlyExistsOnInterfaceA) return canBeTwoInterfaces.A
else return canBeTwoInterfaces.B
}
However, in the codebase I'm working with, the interfaces I'm needing to check mostly consist optional parameters. Plus, someone else on my team might suddently change the names names without me knowing. If this sounds like the codebase you're working in, then the function below is much safer.
Like I said earlier, this might strike many as being a very obvious thing to do. Nonetheless, it is not obvious to know when and where to apply a given solution, regardless of whether it happens to be a brutally simple one like below.
This is what I would do:
const simpleFunction = (
canBeTwoInterfaces: interfaceA | interface B,
whichInterfaceIsIt: 'interfaceA' | 'interfaceB'
) => {
if (whichInterfaceIsIt === 'interfaceA') return canBeTwoInterface.A
else return canBeTwoInterfaces.B
}
You can also send multiple inputs to child components, having one be a discriminator, and the other being the actual data, and checking the discriminator in the child component like this:
#Input() data?: any;
#Input() discriminator?: string;
ngOnInit(){
if(this.discriminator = 'InterfaceAName'){
//do stuff
}
else if(this.discriminator = 'InterfaceBName'){
//do stuff
}
}
Obviously you can move this into wherever it is applicable to use, like an ngOnChanges function or a setter function, but the idea still stands. I would also recommend trying to tie an ngModel to the input data if you want a reactive form. You can use these if statements to set the ngModel based on the data being passed in, and reflect that in the html with either:
<div [(ngModel)]={{dataModel}}>
<div *ngFor="let attr of (data | keyvalue)">
<!--You can use attr.key and attr.value in this situation to display the attributes of your interface, and their associated values from the data -->
</div>
</div>
Or This Instead:
<div *ngIf = "model == 'InterfaceAName'">
<div>Do This Stuff</div>
</div>
<div *ngIf= "model == 'IntefaceBName'">
<div>Do this instead</div>
</div>
(You can use attr.key and attr.value in this situation to display the attributes of your interface, and their associated values from the data)
I know the question is already answered, but I thought this might be useful for people trying to build semi-ambiguous angular forms. You can also use this for angular material modules (dialog boxes for example), by sending in two variables through the data parameter--one being your actual data, and the other being a discriminator, and checking it through a similar process. Ultimately, this would allow you to create one form, and shape the form around the data being flowed into it.
Working with string literals is difficult because if you want to refactor you method or interface names then it could be possible that your IDE don't refactor these string literals.
I provide you mine solution which works if there is at least one method in the interface
export class SomeObject implements interfaceA {
public methodFromA() {}
}
export interface interfaceA {
methodFromA();
}
Check if object is of type interface:
const obj = new SomeObject();
const objAsAny = obj as any;
const objAsInterfaceA = objAsAny as interfaceA;
const isObjOfTypeInterfaceA = objAsInterfaceA.methodFromA != null;
console.log(isObjOfTypeInterfaceA)
Note: We will get true even if we remove 'implements interfaceA' because the method still exists in the SomeObject class
Simple workaround solution having the same drawbacks as the selected solution, but this variant catches JS errors, only accepts objects as parameter, and has a meaningful return value.
interface A{
member:string;
}
const implementsA = (o: object): boolean => {
try {
return 'member' in o;
} catch (error) {
return false;
}
}
const a:any={member:"foobar"};
implementsA(a) && console.log("a implements A");
// implementsA("str"); // causes TS transpiler error
I know the question is a bit old, but just my 50 cents. This worked for me:
const container: Container = icc.controlComponent as unknown as Container;
if (container.getControlComponents) {
this.allControlComponents.push(...container.getControlComponents());
}
Container is the interface, and icc.controlComponent is the object I wanted to check, and getControlComponents is a method from Container interface.
Here's the solution I came up with using classes and lodash: (it works!)
// TypeChecks.ts
import _ from 'lodash';
export class BakedChecker {
private map: Map<string, string>;
public constructor(keys: string[], types: string[]) {
this.map = new Map<string, string>(keys.map((k, i) => {
return [k, types[i]];
}));
if (this.map.has('__optional'))
this.map.delete('__optional');
}
getBakedKeys() : string[] {
return Array.from(this.map.keys());
}
getBakedType(key: string) : string {
return this.map.has(key) ? this.map.get(key) : "notfound";
}
}
export interface ICheckerTemplate {
__optional?: any;
[propName: string]: any;
}
export function bakeChecker(template : ICheckerTemplate) : BakedChecker {
let keys = _.keysIn(template);
if ('__optional' in template) {
keys = keys.concat(_.keysIn(template.__optional).map(k => '?' + k));
}
return new BakedChecker(keys, keys.map(k => {
const path = k.startsWith('?') ? '__optional.' + k.substr(1) : k;
const val = _.get(template, path);
if (typeof val === 'object') return val;
return typeof val;
}));
}
export default function checkType<T>(obj: any, template: BakedChecker) : obj is T {
const o_keys = _.keysIn(obj);
const t_keys = _.difference(template.getBakedKeys(), ['__optional']);
return t_keys.every(tk => {
if (tk.startsWith('?')) {
const ak = tk.substr(1);
if (o_keys.includes(ak)) {
const tt = template.getBakedType(tk);
if (typeof tt === 'string')
return typeof _.get(obj, ak) === tt;
else {
return checkType<any>(_.get(obj, ak), tt);
}
}
return true;
}
else {
if (o_keys.includes(tk)) {
const tt = template.getBakedType(tk);
if (typeof tt === 'string')
return typeof _.get(obj, tk) === tt;
else {
return checkType<any>(_.get(obj, tk), tt);
}
}
return false;
}
});
}
custom classes:
// MyClasses.ts
import checkType, { bakeChecker } from './TypeChecks';
class Foo {
a?: string;
b: boolean;
c: number;
public static _checker = bakeChecker({
__optional: {
a: ""
},
b: false,
c: 0
});
}
class Bar {
my_string?: string;
another_string: string;
foo?: Foo;
public static _checker = bakeChecker({
__optional: {
my_string: "",
foo: Foo._checker
},
another_string: ""
});
}
to check the type at runtime:
if (checkType<Bar>(foreign_object, Bar._checker)) { ... }
Because the type is unknown at run-time, I wrote code as follows to compare the unknown object, not against a type, but against an object of known type:
Create a sample object of the right type
Specify which of its elements are optional
Do a deep compare of your unknown object against this sample object
Here's the (interface-agnostic) code I use for the deep compare:
function assertTypeT<T>(loaded: any, wanted: T, optional?: Set<string>): T {
// this is called recursively to compare each element
function assertType(found: any, wanted: any, keyNames?: string): void {
if (typeof wanted !== typeof found) {
throw new Error(`assertType expected ${typeof wanted} but found ${typeof found}`);
}
switch (typeof wanted) {
case "boolean":
case "number":
case "string":
return; // primitive value type -- done checking
case "object":
break; // more to check
case "undefined":
case "symbol":
case "function":
default:
throw new Error(`assertType does not support ${typeof wanted}`);
}
if (Array.isArray(wanted)) {
if (!Array.isArray(found)) {
throw new Error(`assertType expected an array but found ${found}`);
}
if (wanted.length === 1) {
// assume we want a homogenous array with all elements the same type
for (const element of found) {
assertType(element, wanted[0]);
}
} else {
// assume we want a tuple
if (found.length !== wanted.length) {
throw new Error(
`assertType expected tuple length ${wanted.length} found ${found.length}`);
}
for (let i = 0; i < wanted.length; ++i) {
assertType(found[i], wanted[i]);
}
}
return;
}
for (const key in wanted) {
const expectedKey = keyNames ? keyNames + "." + key : key;
if (typeof found[key] === 'undefined') {
if (!optional || !optional.has(expectedKey)) {
throw new Error(`assertType expected key ${expectedKey}`);
}
} else {
assertType(found[key], wanted[key], expectedKey);
}
}
}
assertType(loaded, wanted);
return loaded as T;
}
Below is an example of how I use it.
In this example I expect the JSON contains an array of tuples, of which the second element is an instance of an interface called User (which has two optional elements).
TypeScript's type-checking will ensure that my sample object is correct, then the assertTypeT function checks that the unknown (loaded from JSON) object matches the sample object.
export function loadUsers(): Map<number, User> {
const found = require("./users.json");
const sample: [number, User] = [
49942,
{
"name": "ChrisW",
"email": "example#example.com",
"gravatarHash": "75bfdecf63c3495489123fe9c0b833e1",
"profile": {
"location": "Normandy",
"aboutMe": "I wrote this!\n\nFurther details are to be supplied ..."
},
"favourites": []
}
];
const optional: Set<string> = new Set<string>(["profile.aboutMe", "profile.location"]);
const loaded: [number, User][] = assertTypeT(found, [sample], optional);
return new Map<number, User>(loaded);
}
You could invoke a check like this in the implementation of a user-defined type guard.

Comparison class constructor parameter between Typescript and Javascript ES6, ES.NEXT

I've been studying TypeScript as the requirement for Angular2/5 and I encountered some doubts.
A couple months ago I also upgraded my knowledge of JS ES6 and so on.
I'm quite sure that I'm not in wrong but for a full understanding of TS I'll ask you anyway.
This is the code that you can find here:
https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties
{
class Octopus {
readonly numberOfLegs: number = 8;
constructor(readonly classAttr: string) { ... } // name attribute
}
console.log( (new Octopus('spicylemoned')).classAttr ); // It works
}
Is there a way in recent JS updates to define attributes inside class' constructor in vanilla like so in TS? (w/o implicitly assigning it through this instance)
{
class Test{
constructor({ passedVar : classAttr } ) { ... };
};
console.log( (new Test({ passedVar : 'sweety' })).classAttr );
//it doesnt work
}
In JS there isn't any similar syntax. The easiest way to do this is to use Object.assign():
class Test {
constructor({ passedVar }) {
Object.assign(this, { classAttr: passedVar });
}
}
console.log(new Test({ passedVar: 'sweety' }).classAttr);
This way is better if you have many attributes; if there's only one, you can just assign in to this: this.classAttr = passedVar.
I thought of a very contrived way of passing an object initializer to a class using a custom Proxy. It may seem verbose, but the Proxy can be re-used for as many class definitions as you want:
// mem initializer list
const list = (() => {
let props = new WeakMap()
// this: class instance
// o: initializer object
function initialize(o = {}) {
if (props.has(this)) {
return this[props.get(this)] = o;
}
return new Proxy(o, {
get: (o, p) => (props.set(this, p), this[p] = o[p])
});
}
// .call.bind() allows initialize(this, o)
// to pass a context and one argument
// instead of two arguments
return initialize.call.bind(initialize);
})();
// example usage
class Test {
constructor (o, { classAttr = list(this, 'foo'), undefinedParameter = list(this, 'bar') } = list(this, o)) {
/* ... */
}
}
console.log(new Test({ classAttr: 'sweety', excessParameter: 'oops' }));
console.log(new Test());
Notice that undefinedParameter is always initialized with its default value 'bar', while the excessParameter is never added to the class instance.
Note: this is by no means a performant solution. It is a pure gimmick, but demonstrates that it is possible to somewhat implicitly initialize a class instance.
A downside to using this approach is that there is no way to intercept default parameters like
{ classAttr = 'defaultValue' }
so instead you have to use the somewhat unsavory syntax
{ classAttr = list(this, 'defaultValue') }
in order to provide default parameters.
If you are extending another class, you must initialize the instance with a Proxy returned by list(super(), o):
const list = (() => {
let props = new WeakMap()
function initialize(o = {}) {
if (props.has(this)) {
return this[props.get(this)] = o;
}
return new Proxy(o, {
get: (o, p) => (props.set(this, p), this[p] = o[p])
});
}
return initialize.call.bind(initialize);
})();
// example usage
class Test extends Object {
constructor (o, { classAttr = list(this, 'foo'), undefinedParameter = list(this, 'bar') } = list(super(), o)) {
/* ... */
}
}
console.log(new Test({ classAttr: 'sweety', excessParameter: 'oops' }));
console.log(new Test());
The syntax for an ES6 class forbids the assignment of properties directly on a prototype object within the class definition.
You are allowed to use a getter to achieve the same effect. Only defining a getter will also make it readonly.
However, there is no implicit syntax within vanilla javascript to handle typechecks, meaning this must be done manually using typeof <variable> === '<primitiveType>' or <variable> instanceof <Class> within the function.
Using the Octopus class this will result in the following:
class Octopus{
constructor(classAttr){
if (typeof classAttr !== 'string' && typeof classAttr !== 'object') {
throw new TypeError('classAttr is neither a string nor an object');
}
if (typeof classAttr === 'object') {
Object.assign(this,classAttr);
}
else {
this.classAttr = classAttr;
}
}
//readonly, because there is no setter
get numberOfLegs(){
return 8;
}
}
let octopus = new Octopus('bigEyed');
console.log(octopus.numberOfLegs); // 8
console.log(octopus.classAttr); // bigEyed

Default value of Type in Generic classes in Typescript

I need to set a default value of a variable based on its type in Typescript generic classes like below
class MyClass<T>{
myvariable: T // Here I want to set the value of this variable
// with the default value of the type passed in 'T'
}
For example if the T is number then the default value of the variable myvariable should be "0", similarly for string it should be empty string and so on..
You can't do that as the actual type which is T will only be known at runtime.
What you can do:
abstract class MyClass<T> {
myvariable: T;
constructor() {
this.myvariable = this.getInitialValue();
}
protected abstract getInitialValue(): T;
}
Now you just extend this class, like so:
class MyStringClass extends MyClass<string> {
protected getInitialValue(): string {
return "init-value";
}
}
Edit
What you're asking for can not be done because T only exists in the typescript realm, and it doesn't "survive" the compilation process.
For example, this:
class MyClass<T> {
myvariable: T;
constructor(value: T) {
this.myvariable = value;
}
}
Compiles into:
var MyClass = (function () {
function MyClass(value) {
this.myvariable = value;
}
return MyClass;
}());
As you can see, in the compiled version there's no T, so you can't use that information at runtime in order to generate a default value.
Another solution is to have a map of default values:
var defaultValues = {
"string": "",
"number": 0,
"boolean": false
}
class MyClass<T> {
myvariable: T;
constructor(value: T) {
this.myvariable = value;
}
}
let a = new MyClass<string>(defaultValues.string);
let b = new MyClass<boolean>(defaultValues.boolean);
You can also use static factory methods:
class MyClass<T> {
myvariable: T;
constructor(value: T) {
this.myvariable = value;
}
static stringInstance(): MyClass<string> {
return new MyClass<string>("");
}
static numberInstance(): MyClass<number> {
return new MyClass<number>(0);
}
static booleanInstance(): MyClass<boolean> {
return new MyClass<boolean>(false);
}
}
let a = MyClass.stringInstance();
let b = MyClass.booleanInstance();
T is lost at runtime, so you need to pass in the type like a value. And you can do that by passing the constructor.
When I have a similar need, I usually use an interface like this:
interface Type<T> {
new() : T;
}
And create MyClass like so:
class MyClass<T>{
myvariable: T;
constructor(type: Type<T>) {
this.myvariable = new type();
}
}
Then I can use MyClass like so:
let myinstance = new MyClass(TheOtherType);
It works for classes, but not for built-ins like string and number.
TheOtherType is the constructor of a class such as:
class TheOtherType {
}
Well I guess I came up with a possible solution. Anyway I must say that Nitzan Tomer is right and the class type is not available in runtime. This is the funny side of TS :)
Here you can check the type of the objects you get in and then set a default value. You could also change the place and the objects to check in order to do it the way you want but I guess it could be a good starting point for you. Notice that you have to do a double cast because TS cannot guarantee that types are compatible. I haven't tried it yet but it compiles well
class MyClass<T>{
myvariable: T
constructor(param: any) {
if (typeof param === 'string') {
this.myvariable = <T><any> "";
}
else if (typeof param === 'number') {
this.myvariable = <T><any> 0;
}
}
}

Typescript: Avoid comparison by reference

I need to store a list of points and check if a new point is already included in that list
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
window.onload = () => {
var points : Point[] = [];
points.push(new Point(1,1));
var point = new Point(1,1);
alert(points.indexOf(point)); // -1
}
Obviously typescript uses comparison by reference but in this case that doesn't make sense. In Java or C# I would overload the equals method, in typescript that doesn't seem to be possible.
I considered to loop through the array with foreach and check each entry for equality, but that seems rather complicated and would bloat the code.
Is there something like equals in typescript ? How can I implement my own comparisons ?
Typescript doesn't add any functionality to JavaScript. It's just "typed" and some syntax improvements.
So, there's not a way to override equals in an equivalent way to what you might have done in C#.
However, you would have ended up likely using a Hash or a strongly-typed Dictionary in C# to do an efficient lookup (in addition to the array potentially), rather than using an "index of" function.
For that, I'd just suggest you use an associative array structure to store the Points.
You'd do something like:
class Point {
constructor(public x:Number = 0,
public y:Number = 0 ) {
}
public toIndexString(p:Point):String {
return Point.pointToIndexString(p.x, p.y);
}
static pointToIndexString(x:Number, y:Number):String {
return x.toString() + "#" + y.toString();
}
}
var points:any = {};
var p: Point = new Point(5, 5);
points[p.toIndexString()] = p;
If a Point doesn't exist, checking the points associative array will returned undefined.
A function wrapping the array would be simple:
function findPoint(x:Number, y:Number):Point {
return points[Point.pointToIndexString(x, y)];
}
Looping through all points would be easy to:
// define the callback (similar in concept to defining delegate in C#)
interface PointCallback {
(p:Point):void;
}
function allPoints(callback:PointCallback):void {
for(var k in points) {
callback(points[k]);
}
}
allPoints((p) => {
// do something with a Point...
});
You could wrap the collection in a PointList that only allows Point objects to be added via an add method, which checks to ensure no duplicates are added.
This has the benefit of encapsulating the "No duplicates" rule in a single place, rather than hoping that all calling code will check before adding a duplicate, which would duplicate the rule in many places.
class Point {
constructor(public x: number, public y: number) {
}
}
class PointList {
private points: Point[] = [];
get length() {
return this.points.length;
}
add(point: Point) {
if (this.exists(point)) {
// throw 'Duplicate point';
return false;
}
this.points.push(point);
return true;
}
exists(point: Point) {
return this.findIndex(point) > -1;
}
findIndex(point: Point) {
for (var i = 0; i < this.points.length; i++) {
var existingPoint = this.points[i];
if (existingPoint.x === point.x && existingPoint.y === point.y) {
return i;
}
}
return -1;
}
}
var pointList = new PointList();
var pointA = new Point(1, 1);
var pointB = new Point(1, 1);
var pointC = new Point(1, 2);
pointList.add(pointA);
alert(pointList.length); // 1
pointList.add(pointB);
alert(pointList.length); // 1
pointList.add(pointC);
alert(pointList.length); // 2
One thing you can do is try out linq.js. With that, you can do something like this:
var foundPoint = Enumerable.From(points)
.SingleOrDefault(function(p) {
return p.x == targetX && p.y == targety;
});
... you could then just implement this function on your object
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
static equals(points: Point[], candidate: Point): boolean {
var foundPoint = Enumerable.From(points)
.SingleOrDefault((p: Point): boolean => {
return p.x == candidate.x && p.y == candidate.y;
});
return foundPoint != null;
}
}
... and use it like this
var points = createPointsArray();
var point = getSomePoint();
// is point already in array?
var isPointInPoints = Point.equals(points, point)
Solution using a Set
I thought that a Set would be a perfect fit for this kind of problem since:
A value in the Set may only occur once; it is unique in the Set's collection. — MDN
However, you can still add [0, 1, 2, 3] multiples times to a Set (see this answer) since Sets use the SameValueZero(x, y) comparison algorithm to compare values (see also this answer). That's why you have to implement your own version of a Set.
Custom Set implemenation
I ported Johnny Bueti's solution to Sets. I don't overwrite any prototype as that is considered bad practice and can have unwanted side-effects.
Inside util.ts:
export interface Equatable {
/**
* Returns `true` if the two objects are equal, `false` otherwise.
*/
equals(object: any): boolean
}
export class SetCustomEquals<T extends Equatable> extends Set<T>{
add(value: T) {
if (!this.has(value)) {
super.add(value);
}
return this;
}
has(otherValue: T): boolean {
for (const value of this.values()) {
if (otherValue.equals(value)) {
return true;
}
}
return false;
}
}
Usage
import { Equatable, SetCustomEquals } from "./util";
class MyClass implements Equatable {
equals(other: MyClass): boolean {
... (your custom code)
}
}
const mySet= new SetCustomEquals<MyClass>();
const myObject = new MyClass();
mySet.add(myObject);
I would implement my own comparison method. In this instance I'm extending the Array.prototype - which I would not suggest unless you know exactly what you're doing - but you can very well create your own class that inherits from Array and implements a .contains() method like the one defined below.
interface Equatable {
/**
* Returns `true` if the two objects are equal, `false` otherwise.
*/
equals(object: any): boolean
}
class Point implements Equatable {
public equals(toPoint: Point): boolean {
return this.x === toPoint.x && this.y === toPoint.y;
}
}
// Augment the global scope
declare global {
interface Array<T extends Equatable> {
/**
* Returns `true` if an object is found, `false` otherwise. This method uses the `.equals()` method to compare `Equatable` objects for equality.
*/
contains(object: T): boolean
}
}
// Extend the Array.prototype.
Array.prototype.contains = function(object: Equatable) {
return this.findIndex(element => {
return element.equals(object);
}) !== -1;
}
The Equatable interface allows you to extend this behaviour to any other object:
class User implements Equatable {
public equals(toUser: User): boolean {
return this.uuid === toUser.uuid;
}
}

How does prototype extend on typescript?

i extended function prototype but typescript doesn't recognize it.
Function.prototype.proc = function() {
var args, target, v;
var __slice = [].slice;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
target = this;
while (v = args.shift()) {
target = target(v);
}
return target;
};
// generated by coffee-script
var foo: (number) => (string) => number
= (a) => (b) => a * b.length;
console.log(foo.proc("first", "second"))
result: tsc -e
The property 'proc' does not exist on value of type 'Function'
how do i extend this object?
There is a Function interface in the standard typescript lib which declares the members of Function objects. You will need to declare proc as a member of that interface with your own add on like the following:
interface Function {
proc(...args: any[]): any;
}
This interface will need to be referenced from anywhere you intend to use 'proc'.
Like this:
declare global {
interface Function {
proc() : any;
}
}
Without 'declare global' it doesn't work.
That's how module augmentation works in recent TypeScript versions. Check out the documentation and scroll down to the Module augmentation section.
Static method
declare global {
interface NumberConstructor {
formatCurrency(num: number): string;
}
}
export const formatCurrency = (num: number) => {
if (!num) return '$0';
return '$' + num.toFixed(0).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
};
Number.formatCurrency = formatCurrency;
non-static method
declare global {
interface Number {
formatCurrency: () => string;
}
}
Number.prototype.formatCurrency = function() : string {
return '$' + this.toFixed(0).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
}
Just adding that if you're trying to add define something that's already declared, then this is the typesafe way of doing so, that also guards against buggy for in implementations.
export const augment = <U extends (string|symbol), T extends {[key :string] :any}>(
type :new (...args :any[]) => T,
name :U,
value :U extends string ? T[U] : any
) => {
Object.defineProperty(type.prototype, name, {writable:true, enumerable:false, value});
};
Which can be used to safely polyfill. Example
//IE doesn't have NodeList.forEach()
if (!NodeList.prototype.forEach) {
//this errors, we forgot about index & thisArg!
const broken = function(this :NodeList, func :(node :Node, list :NodeList) => void) {
for (const node of this) {
func(node, this);
}
};
augment(NodeList, 'forEach', broken);
//better!
const fixed = function(this :NodeList, func :(node :Node, index :number, list :NodeList) => void, thisArg :any) {
let index = 0;
for (const node of this) {
func.call(thisArg, node, index++, this);
}
};
augment(NodeList, 'forEach', fixed);
}
Unfortunately it can't typecheck your Symbols due to a limitation in current TS, and it won't yell at you if the string doesn't match any definition for some reason, I'll report the bug after seeing if they're already aware.
I am adding this to advise against adding prototypes like the example shown in question since many people view this question. Add it as follows:
interface Function {
proc(...args: any[]): any;
}
Object.defineProperty(Function.prototype, 'proc', { value: function(arg: any[]) {
// Your function body
}});
The reason is if you add it to the prototype directly, it could get enumerated if an instance of that function get's enumerated over. for i in ... Now this block could be in a code you do not control (recently happened to me), so it is best to keep your code as safe as possible.

Categories

Resources