Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typescript support #348

Merged
merged 7 commits into from
Oct 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"homepage": "https://stampit.js.org",
"main": "dist/stampit.min.js",
"minified:main": "dist/stampit.min.js",
"typings": "./src/stampit.d.ts",
"types": "./types/index.d.ts",
"keywords": [
"object",
"prototype",
Expand All @@ -28,17 +28,19 @@
},
"dependencies": {},
"devDependencies": {
"benchmark": "^2.1.1",
"benchmark": "^2.1.4",
"browserify": "^16.5.0",
"check-compose": "^5.0.0",
"eslint": "^6.4.0",
"gzip-size-cli": "^2.1.0",
"eslint": "^6.5.1",
"gzip-size-cli": "^3.0.0",
"lodash": "^4.17.15",
"mkdirp": "^0.5.1",
"nyc": "^14.1.1",
"require-all": "^2.2.0",
"tape": "^4.2.2",
"uglify-js": "^2.8.29"
"require-all": "^3.0.0",
"tape": "^4.11.0",
"tslint": "^5.20.0",
"typescript": "^3.6.4",
"uglify-js": "^3.6.2"
},
"scripts": {
"cov": "nyc npm run test",
Expand All @@ -51,7 +53,7 @@
"build": "npm run minify",
"ci": "npm run test",
"check": "npm run test && npm run lint",
"minify": "mkdirp ./dist/ && uglifyjs src/stampit.js -c collapse_vars,evaluate=false,screw_ie8,unsafe,loops=false,keep_fargs=false,pure_getters,unused,dead_code,keep_fnames=[\"'stampit','Stamp'\"] -m --reserved stampit,Stamp -o dist/stampit.min.js",
"minify": "mkdirp ./dist/ && uglifyjs src/stampit.js -c collapse_vars,evaluate=false,ie8=false,unsafe,loops=false,keep_fargs=false,pure_getters,unused,dead_code,keep_fnames=[\"'stampit','Stamp'\"] -m --reserved stampit,Stamp -o dist/stampit.min.js",
"preversion": "npm run check",
"postversion": "V=`node -e \"process.stdout.write(require('./package.json').version)\"` && sed s/VERSION/$V/g dist/stampit.min.js > dist/tmp && rm dist/stampit.min.js && mv dist/tmp dist/stampit.min.js",
"postminify": "ls -l dist/ && echo GZIP size: && gzip-size --raw dist/stampit.min.js"
Expand Down
308 changes: 308 additions & 0 deletions test/tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
import stampit from '../types';

/** Import 'stampit' module */
// import stampit from 'stampit';

/** Import 'stampit' as ... module */
// import * as Stamp from 'stampit';

/** selective import from module */
// import {
// compose,
// composers,
// conf,
// configuration,
// deepConf,
// deepConfiguration,
// deepProperties,
// deepProps,
// deepStatics,
// init,
// initializers,
// methods,
// properties,
// propertyDescriptors,
// props,
// staticDeepProperties,
// staticProperties,
// staticPropertyDescriptors,
// version,
// } from 'stampit';

const a = stampit().init(function(options) {
const a = options.args[0];
this.getA = () => {
return a;
};
});
a(); // Object -- so far so good.
a().getA(); // "a"

const b = stampit().init(function() {
const a = 'b';
this.getB = () => {
return a;
};
});

const c = stampit.compose(a, b);
const foo = c(); // we won't throw this one away...
foo.getA(); // "a"
foo.getB(); // "b"

// Here's a mixin with public methods, and some props:
const membership = stampit({
methods: {
// members: {},
add(member: any) {
this.members[member.name] = member;
return this;
},
getMember(name: any) {
return this.members[name];
}
},
props: {
members: {}
}
});

// Let's set some defaults:
const defaults = stampit().props({
name: 'The Saloon',
specials: 'Whisky, Gin, Tequila'
});

// Classical inheritance has nothing on this. No parent/child coupling. No deep inheritance hierarchies.
// Just good, clean code reusability.
const bar = stampit.compose(defaults, membership);
// Note that you can override props on instantiation:
const myBar = bar({ name: 'Moe\'s' });
// Silly, but proves that everything is as it should be.
myBar.add({ name: 'Homer' }).open().getMember('Homer');

const myStamp = stampit().methods({
foo() {
return 'foo';
},
methodOverride() {
return false;
}
}).methods({
bar() {
return 'bar';
},
methodOverride() {
return true;
}
});

myStamp.deepProps({
foo: { bar: 'bar' },
refsOverride: false
}).props({
bar: 'bar',
refsOverride: true
});

myStamp.init(function() {
const secret = 'foo';

this.getSecret = () => {
return secret;
};
}).init(function() {
this.a = true;
}).init(function() {
this.b = true;
}, function() {
this.c = true;
});

let obj = myStamp.create();
obj.getSecret && obj.a && obj.b && obj.c; // true

const newStamp = stampit({ props: { defaultNum: 1 } }).compose(myStamp);

const obj1 = stampit().methods({
a() {
return 'a';
}
}, {
b() {
return 'b';
}
}).create();

const obj2 = stampit().props({
a: 'a'
}, {
b: 'b'
}).create();

obj = defaults.compose(newStamp, membership).create();

// The old constructor / class thing...
const Constructor = function Constructor() {
this.thing = 'initialized';
};
Constructor.prototype.foo = function foo() {
return 'foo';
};

// A new stamp to compose with...
const newskool = stampit().methods({
bar: function bar() {
return 'bar';
}
// your methods here...
}).init(function() {
this.baz = 'baz';
});

// Now you can compose those old constructors just like you could
// with any other stamp...
const myThing = stampit.compose(newskool);

const t = myThing();

t.thing; // 'initialized',

t.foo(); // 'foo',

t.bar(); // 'bar'

interface SomeStampInstance {
a: string;
b: string;
}

// Test import of stamp type
interface SomeStamp extends stampit.Stamp<SomeStampInstance> {
(params: { a: number; b: boolean}): SomeStampInstance;
}

const SomeStamp = stampit<SomeStamp>()
.init(function(params: { a: number; b: boolean}) {
this.a = '' + a; // this SomeStampInstance
this.b = '' + b;
});

SomeStamp({ a: 1, b: false }); // $ExpectType SomeStampInstance
SomeStamp({ a: 1, b: false }).a; // $ExpectType string

const d: stampit.ExtendedDescriptor<{}> = {
methods: {},
properties: {},
deepProperties: {},
propertyDescriptors: {},
initializers: [],
staticProperties: {},
staticDeepProperties: {},
staticPropertyDescriptors: {},
composers: [],
configuration: {},
deepConfiguration: {},
name: '',
// shorthands
props: {},
deepProps: {},
init: [],
statics: {},
deepStatics: {},
conf: {},
deepConf: {},
};

// The `.compose()` method
const compose = stampit.compose; // $ExpectType typeof stampit

const stampUntyped = compose(); // $ExpectType Stamp<any>
stampUntyped(); // $ExpectType any
// const stampAny = compose<any>(); // $ExpectType Stamp<any>
// stampAny(); // $ExpectType any
const stampBigint = compose<bigint>(); // $ExpectType never
const stampBoolean = compose<boolean>(); // $ExpectType never
const stampNull = compose<null>(); // $ExpectType never
const stampNumber = compose<number>(); // $ExpectType never
const stampString = compose<string>(); // $ExpectType never
const stampSymbol = compose<symbol>(); // $ExpectType never
const stampUndefined = compose<undefined>(); // $ExpectType never
const stampUnknown = compose<unknown>(); // $ExpectType Stamp<unknown>
stampUnknown(); // $ExpectType unknown
const stampNever = compose<never>(); // $ExpectType never

// Declare interface of objects created by stamps
interface Object0 {
action: () => void;
value: number;
}

interface Object1 {
reaction: () => void;
property: number;
}

const stampObject0 = compose<Object0>(); // $ExpectType Stamp<Object0>
stampObject0(); // $ExpectType Object0

const stampObject1 = compose<Object1>(); // $ExpectType Stamp<Object1>
stampObject1(); // $ExpectType Object1

// Define a typed ExtendedDescriptor and benefit its properly typed `this` in `methods`
const descriptor0: stampit.ExtendedDescriptor<Object0> = {
methods: {
logLocalThis() {
this; // $ExpectType Object0
},
},
properties: {},
deepProperties: {},
propertyDescriptors: {},
staticProperties: {},
staticDeepProperties: {},
staticPropertyDescriptors: {},
initializers: [
function(options, context) {
this; // $ExpectType Object0
return context.instance;
}
],
composers: [
(parameters) => {
return parameters.stamp;
}
],
configuration: {},
deepConfiguration: {}
};

const stampDescriptor0 = compose<typeof descriptor0>(); // $ExpectType Stamp<Object0>
stampDescriptor0(); // $ExpectType Object0

// check typed stamps... with untyped descriptor (`this` isn't typed in `methods`)
// inline type assertion is still possible though
const stampUntypedDescriptor0 = compose<Object0>(/* <stampit.ExtendedDescriptor<Object0>> */ {
methods: {
logLocalThis() {
this; // $ExpectType any
},
},
} /* as stampit.ExtendedDescriptor<Object0> */);
stampUntypedDescriptor0; // $ExpectType Stamp<Object0>
stampUntypedDescriptor0(); // $ExpectType Object0

// Check a stamp's `.compose()` method
const stamp1 = stampObject0.compose<Object1>(); // $ExpectType Stamp<Object1>
stamp1(); // $ExpectType Object1

// Define an extended stamp.
// The type of created object cannot be changed afterward
// Lazy interface composition can be done using the `&` intersection operator
interface ExtendedStamp extends stampit.Stamp<Object0 & Object1> {
decompose: () => unknown;
}

// Invoke `.compose()` method with the type of the extended stamp
const extendedStamp0 = compose<ExtendedStamp>(); // $ExpectType ExtendedStamp
extendedStamp0(); // $ExpectType Object0 & Object1
Loading