Skip to content

Commit 251a5c8

Browse files
wgao19niieani
authored andcommitted
Add function statics and object callable properties (#55)
* Add function statics and object callable properties * Update function statics and object callables - Use simpler examples - Add TypeScript on object callables * Add TypeScript example for overloading callable * Updates on callable properties * Updates on callable properties - Move to "same syntax" - Add a few library definitions as reference * Add back the line # Read-only Types that got deleted by accident * Not sure how TS supports function statics * Resolve PR discussion and add function statics
1 parent ca7b36c commit 251a5c8

File tree

1 file changed

+118
-0
lines changed

1 file changed

+118
-0
lines changed

README.md

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,8 @@ add("1", "1"); // Ok
519519
add(1, "1"); // Error
520520
```
521521

522+
It is also possible to create function overloads using callable property syntax, see the section [Object callable property](#object-callable-property).
523+
522524
### TypeScript
523525

524526
TypeScript supports both function and method overloading, in both: type definitions (`.d.ts`) and inline alongside code.
@@ -635,6 +637,122 @@ However, Flow implementation is stricter in this case, as B have a property that
635637

636638
Most of the syntax of Flow and TypeScript is the same. TypeScript is more expressive for certain use-cases (advanced mapped types with keysof, readonly properties), and Flow is more expressive for others (e.g. `$Diff`).
637639

640+
## Object callable property
641+
642+
The basic syntax are the same, except Flow has special syntax for the internal call property slot.
643+
644+
Both can be used to annotate function statics.
645+
646+
### Flow
647+
648+
You can use objects with callable properties as functions: [Try Flow](https://flow.org/try/#0PTAEAEDMBsHsHcBQiAuBPADgU1AMVALygDeiooAFAJQBcoAzigE4CWAdgOaIC+A3IgGNYbRqEh18RaoQB8oAEQALLNDjz+QkSlDLVsOo1adCY6ryA)
649+
650+
```js
651+
type F = {
652+
(): string
653+
};
654+
const f: F = () => "hello";
655+
const hello: string = f();
656+
```
657+
658+
An overloaded function is a function with multiple call signatures.
659+
This is supported by Flow. And we list out the different syntaxes here: [Try Flow](https://flow.org/try/#0PTAEAEDMBsHsHcBQiAuBPADgU1AMVALygDeiooAFAJQBcoAzigE4CWAdgOYA0ZoA2nwDGAQ2jQAuuLoU2AVwC2AIyxMqhAHwNm7brwEixkio1adaW0x0QBfZINhtGoSHXxEKADwD8dOUpWgAD4WOmoEmqTkTFgoskxsoB6gXokAdCiwAMranNSgdADkBQDcNohAA)
660+
661+
```js
662+
type F = {
663+
(): string,
664+
[[call]]: (number) => string,
665+
[[call]](string): string
666+
}
667+
668+
const f: F = (x?: number | string) => {
669+
return x ? x.toString() : '';
670+
}
671+
```
672+
673+
Use call property to annotate function statics: [Try Flow](https://flow.org/try/#0PTAEAEDMBsHsHcBQiAuBPADgU1AWSwLawCWAXlgCYBiAhgMYqwBOxN0AKpjgLygDeiUKDr0AFlgBc-QaACQAbQB2AVwIAjLEwC6Ules0yAvgBoZ8+SOjQtWgBR6NTAJS7Vj04eR1YigM4pQSHpGFjYpfCIySloGZlYOLlBeRSSAPmkhYkhQWwBCINjQ6AA6ETpxJwyhQOC4tlKxHn5PIRbQLJyCkPiG8qwlLVBc7l5lRQosSGJFSkqBatAmLBRlJhSuupKy8QGjGQ2i3p3FQeSkkdAABlAAflAARlBdUAAqGsL4+1AAWgenGSWKzW7269W2-ROiEMQA)
674+
675+
```js
676+
type MemoizedFactorialType = {
677+
cache: {
678+
[number]: number,
679+
},
680+
[[call]](number): number,
681+
}
682+
683+
const factorial: MemoizedFactorialType = n => {
684+
if (!factorial.cache) {
685+
factorial.cache = {}
686+
}
687+
if (factorial.cache[n] !== undefined) {
688+
return factorial.cache[n]
689+
}
690+
factorial.cache[n] = n === 0 ? 1 : n * factorial(n - 1)
691+
return factorial.cache[n]
692+
}
693+
```
694+
695+
Reference:
696+
697+
- [Callable Objects](https://flow.org/en/docs/types/functions/#callable-objects-)
698+
- [immer.js](https://github.com/immerjs/immer/blob/master/src/immer.js.flow) uses it to overload the `produce` (default export) function which has multiple call signatures
699+
- [Styled Components](https://github.com/flow-typed/flow-typed/blob/master/definitions/npm/styled-components_v4.x.x/flow_v0.75.x-/styled-components_v4.x.x.js#L242) uses it to separate cases of being called on a string and wrapping a component
700+
- [Reselect Library Definition](https://github.com/flow-typed/flow-typed/blob/master/definitions/npm/re-reselect_v2.x.x/flow_v0.67.1-/re-reselect_v2.x.x.js) contains massive chunks of overloaded call properties
701+
702+
### TypeScript
703+
704+
You can use objects with callable properties as functions: [TypeScript Playground](https://www.typescriptlang.org/play/#src=type%20F%20%3D%20%7B%0D%0A%20%20()%3A%20string%3B%0D%0A%7D%0D%0Aconst%20foo%3A%20F%20%3D%20()%20%3D%3E%20%22hello%22%3B%0D%0Aconst%20bar%3A%20string%20%3D%20foo()%3B)
705+
706+
```ts
707+
type F = {
708+
(): string;
709+
}
710+
const foo: F = () => "hello";
711+
const bar: string = foo();
712+
```
713+
714+
An overloaded function is a function with multiple call signatures.
715+
This is also supported by TypeScript: [TypeScript Playground](https://www.typescriptlang.org/play/#src=type%20F%20%3D%20%7B%0D%0A%20%20()%3A%20string%2C%0D%0A%20%20(number)%3A%20string%2C%0D%0A%20%20(string)%3A%20string%0D%0A%7D%0D%0A%0D%0Aconst%20f%3A%20F%20%3D%20(x%3F%3A%20number%20%7C%20string)%20%3D%3E%20%7B%0D%0A%20%20return%20x%20%3F%20x.toString()%20%3A%20''%3B%0D%0A%7D%0D%0A)
716+
717+
718+
```ts
719+
type F = {
720+
(): string,
721+
(x: number): string,
722+
(x: string): string
723+
}
724+
725+
const f: F = (x?: number | string) => {
726+
return x ? x.toString() : '';
727+
}
728+
```
729+
730+
Use call property to annotate function statics: [TypeScript Playground](https://www.typescriptlang.org/play/#src=type%20MemoizedFactorialType%20%3D%20%7B%0D%0A%20%20cache%3F%3A%20%7B%0D%0A%20%20%20%20%5Bn%3A%20number%5D%3A%20number%2C%0D%0A%20%20%7D%2C%0D%0A%20%20(n%3A%20number)%3A%20number%2C%0D%0A%7D%0D%0A%0D%0Aconst%20factorial%3A%20MemoizedFactorialType%20%3D%20n%20%3D%3E%20%7B%0D%0A%20%20if%20(!factorial.cache)%20%7B%0D%0A%20%20%20%20factorial.cache%20%3D%20%7B%7D%0D%0A%20%20%7D%0D%0A%20%20else%20if%20(factorial.cache%5Bn%5D%20!%3D%3D%20undefined)%20%7B%0D%0A%20%20%20%20return%20factorial.cache%5Bn%5D%0D%0A%20%20%7D%0D%0A%20%20factorial.cache%5Bn%5D%20%3D%20n%20%3D%3D%3D%200%20%3F%201%20%3A%20n%20*%20factorial(n%20-%201)%0D%0A%20%20return%20factorial.cache%5Bn%5D%0D%0A%7D)
731+
732+
```ts
733+
type MemoizedFactorialType = {
734+
cache?: {
735+
[n: number]: number,
736+
},
737+
(n: number): number,
738+
}
739+
740+
const factorial: MemoizedFactorialType = n => {
741+
if (!factorial.cache) {
742+
factorial.cache = {}
743+
}
744+
else if (factorial.cache[n] !== undefined) {
745+
return factorial.cache[n]
746+
}
747+
factorial.cache[n] = n === 0 ? 1 : n * factorial(n - 1)
748+
return factorial.cache[n]
749+
}
750+
```
751+
752+
Reference:
753+
- [Callable on TypeScript Book](https://github.com/basarat/typescript-book/blob/master/docs/types/callable.md)
754+
755+
638756
## optional parameters
639757

640758
### Flow and TypeScript

0 commit comments

Comments
 (0)