Kotlin/TypeScript/Dart 互換表

(自分用メモ)そこそこ似てる上に同時に使うことが多くて混乱しますよね..

- [Kotlin Playground](https://play.kotlinlang.org/)
- [TypeScript Playground](https://www.typescriptlang.org/play)
- [Dart Pad](https://dartpad.dev/)


### クラス
メンバ・メソッドを持たないクラスC0
メンバmを持つクラスC1
メンバm・メソッドfを持つクラスC2とそのインスタンス化
```kotlin:Kotlin
	class C0
	class C1(val m:Int)
	class C2(val m:Int) {
      fun f() = m
    }
	val c2 = C2(123)
    println(c2.f())
```

```typescript:TypeScript
class C0{}
class C1{
    m: number
    constructor(m: number) {this.m = m}
}
class C2{
    m: number
    constructor(m: number) {this.m = m}
    f() { return this.m }
}
let c2=new C2(123)
console.log(c2.f())
```

```dart:Dart
//TODO
```

### Getter/Setter

```kotlin:Kotlin
var io // 変数毎に定義可
    get() = readln()
    set(s) = println(s)

io = "Hello ${io}." // 標準入力内容を"Hello "に続けて標準出力する。
```

```typescript:TypeScript
class StdIO {
    get io(): string { return process.stdin.read() }
    set io(o: string) { console.log(`Hello:${o}`) }
}
```

```dart:Dart
class Person {
  late int _age;
  int get age => _age;
  set age(int old) => _age = old;
}
```

### 定数/変数
- 定数: ビルド時に値が決定される。定数のみ割り当て可
- イミュータブル: 実行時に1回だけオブジェクトを割り当て可
- ミュータブル: 実行時に1回以上オブジェクトを割り当て可

```kotlin:Kotlin
const val c=1; // 定数、ローカル宣言不可
val imu=c;  // イミュータブル
var mu=imu; // ミュータブル
```

```typescript:TypeScript
// 定数はない?
const imu=1 // イミュータブル
let mu1=imu // ミュータブル(ブロック内から参照可)
var mu2=imu // ミュータブル(関数内から参照可)
```

```dart:Dart
const c=1;  // 定数
final imu=c; // イミュータブル
var mu=imu; // ミュータブル
```

### データ型
nameとageを持つデータ型Personを定義/生成/参照
```kotlin:Kotlin
data class Person(val name:String="noname",val age:Int?,)
val user=Person("shokka",23)
println("${user.name} / $user")
```
```typescript:TypeScript
type Person= { name: string, age:number, }
const user={name:"shokkaa",age:23,} as Person
console.log(`${user.name} / ${user}`)
```

<details><summary>Dart:機能なし(freezed等でヘルパコードを生成する必要あり)</summary>

```dart:Dart/person.dart
import 'package:freezed_annotation/freezed_annotation.dart';
part 'person.freezed.dart'; // freezedで生成

@freezed
class Person with _$Person {
  factory Person({@Default('noname') String name, int? age}) = _Person;
  factory DiscoveryResult.fromJson(Map<String,Object?> map)=> _$DiscoveryResultFromJson(map); // Mapからの変換
}
main() {
  final user = Person(name: "shokkaa");
  print('${user.name} / ${user.toString()}');
}
```
</details>

### 関数定義、関数型と関数リテラル
引数に文字列を与え数値を返す関数f1、
同関数リテラルを格納する変数f2、
```kotlin:Kotlin
fun f1(s:String):Int {return s.toInt()}
fun f1(s:String):Int = s.toInt() // 式表現
val f2:(String)->Int = {s:String->s.toInt()}
val f2 = {s:String->s.toInt()}
```
```typescript:TypeScript
function f1(s:string):number {return parseInt(s)}
const f2:(s:string) => number = function(s:string){parseInt(s)}
const f2:(s:string) => number = (s:string)=>parseInt(s)
const f2 = (s:string)=>parseInt(s)
```
```dart:Dart
int f1(String s) {return int.parse(s);}
int f1(String s) => int.parse(s); // 式表現
int Function(String) f2 = (s) => int.parse(s);
final f2 = (String s) => int.parse(s);
```
### 拡張関数
クラス外で Objectに対する操作を定義する。
文字列の前後に括弧[]を付けて返す拡張関数bracketedと、実行例。
整数の配列の「1番目の要素+2番目の要素」を返す拡張関数add12()

```kotlin:Kotlin
fun String.bracketed()="[$this]"
println("aaa".bracketed()) // [aaa]

fun List<Int>.add12() = this[0]+this[1]
```
```typescript:Typescript
declare global { // modlueを指定する場合は declare module "xxx" 
  interface String {bracketed(): string }
}
String.prototype.bracketed = function(){return `[${this}]`}
console.log("aaa".bracketed()) // [aaa]

interface Array<T> {add12(): T }
Array.prototype.add12 = function(){return this[0]+this[1]}
console.log([1,2,3].add12()) // 3
console.log(['1','2','3'].add12()) // 12 ... number[]だけに適用したいのに...
```
```dart:Dart
extension StringExt on String {
  String bracketed() {return "[$this]";}
}
void main() {
  print("aaa".bracketed());
}

// TODO
```

### 非同期関数
1秒待つ関数

```kotlin:Kotlin
suspend fun sleep1s() = delay(1000) 
fun main() = runBlocking {
    println("1秒寝る")
    sleep1s()
    println("起きた")
}
```

```typescript:TypeScript
async function sleep1s() {
    await new Promise(resolve => setTimeout(resolve, 1000))
}
async function test() {
     console.log("1秒寝る")
     await sleep1s()
     console.log("起きた")
}
test()
```
```dart:Dart
TODO
```


### 非同期反復

```kotlin:Kotlin
// Flow
suspend fun Main(){
    (1..3).asFlow().collect{
        println(it)
        sleep1s()
    }
}
```

```typescript:TypeScript
// ジェネレータ関数
function* gen() { for(const i of [1,2,3]) {yield i}}
async function test() {
    for await (const i of gen()) {
        console.log(i)
        await sleep1s()
    }
}
test()
```
```dart:Dart
TODO
```

### Nullable変数定義/Null許容型
```kotlin:Kotlin
var v:Int? = null
```
```typescript:TypeScript
let v: number| null = null
let v2: number | undefined  // 未定義許容型
```
```dart:Dart
int? v = null;
int? v2; // =null 省略可。 https://dart-lang.github.io/linter/lints/avoid_init_to_null.html
```

### リスト/マップの生成/変換と型
```kotlin:Kotlin
val v:List<Int> = listOf(1,2,3) // 要素数固定の配列を生成
val v2:List<Int> = listOf(0,*v,4) // スプレッド演算子を使用し先頭/末尾に追加 [0,1,2,3,4] 

val m:Map<Int,Int> = listOf(1 to 1,2 to 4,3 to 9).toMap() 
```
```typescript:Typescript
const v=Array<number>(1,2,3) // 配列生成関数
const v=[1,2,3]  // 配列リテラル

const v2=[0,...v,4] // スプレッド演算子を使用し先頭/末尾に追加 [0,1,2,3,4] 

const m = new Map([[1, 1],[2, 4],[3, 9],]) // 要素数固定の配列を生成
const m = new Map([1,2,3].map((e)=>[e,e*e])) // 動的な配列を生成

```
```dart:Dart
const List<int> v = List.generate(3, (i)=>i*i);
const v = [1,2,3];
const v2=[0,...v,4] // スプレッド演算子を使用し先頭/末尾に追加 [0,1,2,3,4] 

```

### 表: 主な型とリテラル
|  | Kotlin | TypeScript | Dart |
|:-:|:-:|:-:|:-:|
| 整数  | - | - | const int i=1; //JS上では有効54bit |
| 32bit整数  | val i:Int=1  | -  | - |
| 64bit整数  | val l:Long=1L  | -  | - |
| 8bit整数  | val by:Byte=1B  | -  |  -  |
| 符号なし8bit整数  | val uby:UInt=1UB  | -  |  -  |
| 実数(float)  | val f:Float=1.2  | -  |   -  |
| 実数(double)  | val d:Double=1.2  | -  |  const double d=1.2;   |
| 数値  | val n:Number=1.2  | const n:number=1.2  | const num n=1.2;  |
| 文字 | `val c:Char='c'` | - | - |  
| 文字列 | `val s:String="str"` | const s:string="str" |  const String s = "str"; |  
| 論理値 | `val b:Boolean = false` | const n:boolean=false | const bool b=false; |  
| 配列/List | `val v:List<Int> =listOf(1,2,3)` | `const v:number[] = Array<number>(1,2,3)` | `List<int> v=<int>[1,2,3];` |  
| 連想配列/Map | `val m:Map<String,Int> = mapOf("a" to 1)` | `const m:Map<string,number> = new Map([["a",1]])` | `  Map<String,int> m = <String,int>{"a":1};` | 
| 基底クラス | `val a:Any=1` | `const a:Object=1` | `Object a=1;` |
| null許容 | `val i:Int?=null` | `const i:any\|null=null` | `int? i=null;` |
| 型チェック |  |  | `if(d is Map){}` |
|  |  |  |  |
|  |  |  |  |
|  |  |  |  |

### List/Map: 変換
```kotlin:Kotlin
```
```typescript:Typescript
```
```dart:Dart
```

### List: ソート
[4,3,5,2,1] => [1,2,3,4,5]
```kotlin:Kotlin
val s = listOf(4,3,5,2,1)
val r1 = s.sorted()
val r2 = s.sortedBy{it}
val r3 = s.sortedWith{a,b->a-b}
```

```dart:Dart
import 'package:collection/collection.dart';
final s = [4,3,5,2,1];
final r = list.sorted((a,b)=>a-b);
```

### イテレーション: 階層をフラット化
[[0,1,2],[3,4]] => [0,1,2,3,4]
```kotlin:Kotlin
val v = listOf(listOf(0,1,2),listOf(3,4))
val r = v.flatMap{it} // ブロックの結果はリストであること
```
```typescript:Typescript/ES2019
const v=[[0,1,2],[3,4]] 
const r=v.flat() // [0,1,2,3,4]
// ネスト深度を指定できる。デフォルト1
const v=[[0,[1,2]],3,[4,[]]]  // 参考:vの型はこう推論される.. (number | (number | number[])[])[]
const r=v.flat(2) // [0,1,2,3,4]
```
```dart:Dart
final v = [[0,1,2],[3,4]];
final r = v.expand((e)=>e);
```
# 表: イテレーション関係
v,v2は整数の配列
mはintをキーにintを値に取る連想配列
itrはintを返すイテレータ

|  | Kotlin | TypeScript | Dart |
|:-:|:-:|:-:|:-:|
| 反復文 - indexed | `for(i in 1 until 3){ }` | `for(let i=0;i<3;i++){ }` |  |
| 反復文 - 要素 | `for(e in v){ }` | `for(e in v){ }` | `for(var e in v){ }` |
| 配列の生成 | `v2=v.map{it*2}` |  `v2=v.map((e)=>e*2)` | `v2= v.map((e)=>e*2) .toList()` |  
| リスト平坦化 | `v2= v.flatMap{listOf(it)}`  |  | `v2= v.expand((e)=>[e]) .toList()` |
| 配列要素の逐次処理 |  |  | `itr = v.map((e)=>e*2)` |
|  |  |  |  |
| イテレータから値を取り出す |  |  |  |
|  |  |  |  |
|  |  |  |  |
|  |  |  |  |
|  |  |  |  |



# 表: 入出力と文字列フォーマッタ
|  | Kotlin | TypeScript | Dart |
|:-:|:-:|:-:|:-:|
| 文字列化して標準出力 | `print(v)` | `console.log(v)` | `print(v);` |
| 差し込み | `"$i:${i+1}"` | `` `${i}:${i+1}` `` | `"$i:${i+1}"` |
| ～ 基数指定 | `i.toString(16)` | `i.toString(16)` |  |
| ～ 16進数 | `"%x".format(i)` | `i.toString(16)` |  |
| ～ 2文字幅(0パディング) | `"%02d".format(i)` | `` `0${i}`.slice(-2)`` | `"$i".padLeft(2,'0')` |
| 文字列➔Byte配列 |  |  | `ba=utf8.encode(s)` |
| Byte配列➔文字列 |  |  |  |
|  |  |  |  |