Skip to content

Commit

Permalink
Implement @kitsuyui/symbolic-prototype
Browse files Browse the repository at this point in the history
 ## Example

```ts
import { SymbolicPrototype } from '@kitsuyui/symbolic-prototype';
const $ = SymbolicPrototype.NumberArray.scale;
const array = [1, 2, 3];
const twice = array[$.scale](2);
console.log(twice); // => [2, 4, 6]
```

 ## Caution

- I do not recommend using this package. It is just for fun.
- This package is experimental.
- This package is not intended to be used in production.
- Breaking changes may be introduced in the future.
  • Loading branch information
kitsuyui committed Dec 28, 2024
1 parent 6e29313 commit b084904
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 2 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ jobs:
string,
luxon-ext,
intended-rollback,
incremental-color-palette
incremental-color-palette,
symbolic-prototype
]

steps:
Expand Down
31 changes: 31 additions & 0 deletions packages/symbolic-prototype/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# @kitsuyui/symbolic-prototype

Using ES6 Symbol to avoid conflict with other libraries and native objects.
In other words it is a modern version of prototype.js.

## Example

```ts
import { SymbolicPrototype } from '@kitsuyui/symbolic-prototype';
const $ = SymbolicPrototype.NumberArray.scale;
const array = [1, 2, 3];
const twice = array[$.scale](2);
console.log(twice); // => [2, 4, 6]
```

## Caution

- I do not recommend using this package. It is just for fun.
- This package is experimental.
- This package is not intended to be used in production.
- Breaking changes may be introduced in the future.

## Install

```sh
npm install @kitsuyui/symbolic-prototype
```

## License

MIT
23 changes: 23 additions & 0 deletions packages/symbolic-prototype/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "@kitsuyui/symbolic-prototype",
"version": "0.0.0",
"license": "MIT",
"author": "Yui Kitsu <[email protected]>",
"description": "Modern prototype extension with Symbol",
"scripts": {
"build": "tsup src/index.ts --clean",
"dev": "pnpm build --watch"
},
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"main": "dist/index.js",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"files": ["dist", "package.json"],
"devDependencies": {}
}
37 changes: 37 additions & 0 deletions packages/symbolic-prototype/src/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
type BaseFunction<T, A extends unknown[]> = (
dummyThis: T,
...args: A
) => unknown

/**
* utility function to bind `this` as the first argument of a function
* remaining arguments are passed as is to the original function
* @param fn
* @returns
*/
export const bindThisAsFirstArgument = <T, A extends unknown[], R>(
fn: (dummyThis: T, ...args: A) => R
): ((this: T, ...args: A) => R) => {
return function (this: T, ...args: A): R {
return fn(this, ...args)
}
}

/**
* extend the prototype of a class with a new method
* @param Target
* @param symbol
* @param fn
*/
export const extend = <T, A extends unknown[], R>(
Target: { prototype: T },
symbol: symbol,
fn: BaseFunction<T, A>
): void => {
Object.defineProperty(Target.prototype, symbol, {
value: bindThisAsFirstArgument(fn),
writable: false,
configurable: false,
enumerable: false,
})
}
14 changes: 14 additions & 0 deletions packages/symbolic-prototype/src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { describe, expect, it, jest } from '@jest/globals'

import { SymbolicPrototype } from '.'

describe('SymbolicPrototype', () => {
it('NumberArray', () => {
expect(SymbolicPrototype.NumberArray).toBeDefined()
expect(SymbolicPrototype.NumberArray.scale).toBeDefined()
const NA = SymbolicPrototype.NumberArray
// example usage
expect([1, 2, 3][NA.scale](2)).toStrictEqual([2, 4, 6])
expect([1, 2, 3][NA.sum]()).toBe(6)
})
})
5 changes: 5 additions & 0 deletions packages/symbolic-prototype/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Symbols } from './number-array'

export const SymbolicPrototype = {
NumberArray: Symbols,
} as const
11 changes: 11 additions & 0 deletions packages/symbolic-prototype/src/number-array/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { describe, expect, it, jest } from '@jest/globals'

import { Symbols } from './index'

describe('NumberArray', () => {
it('scale', () => {
const array: number[] = [1, 2, 3]
const result = array[Symbols.scale](2)
expect(result).toEqual([2, 4, 6])
})
})
24 changes: 24 additions & 0 deletions packages/symbolic-prototype/src/number-array/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { extend } from '../extend'

export const Scale = Symbol('Scale')
export const Sum = Symbol('Sum')

export const Symbols = {
scale: Scale,
sum: Sum,
} as const

declare global {
interface Array<T> {
[Sum](this: Array<number>): number
[Scale](this: Array<number>, scaler: number): number[]
}
}

extend(Array, Scale, (array: number[], scaler: number) => {
return array.map((value) => value * scaler)
})

extend(Array, Sum, (array: number[]) => {
return array.reduce((acc, value) => acc + value, 0)
})
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
"isolatedModules": true,
"noEmit": true
},
"exclude": ["**/*.test.ts"]
"exclude": ["**/*.test.ts", "**/*.spec.ts", "node_modules"]
}

0 comments on commit b084904

Please sign in to comment.