type of typing typescript type
hiroqn NW
https://pbs.twimg.com/profile_images/888409029436817408/zgYun8V5_400x400.jpg
普段の開発
Cycle js(最近xstream
Node
typescript 使い方
jsを書く
なんかいい型ないかな〜
無理しないjsを書く
用語: 気持ち
typescriptのソース読んでないからわからんけど、こうやって解釈すればいいとおもいます
ここに最終更新が20 Jan 2016のdocがあるが、、、
型の定義の仕方、おさらい
code:typescript
// 下の書き方でNameは右の論理を満たす型 <= 気持ち
type Name = ....
// "&" XとYを満たす型
type And = X & Y
// "|" X | Yを満たす型
type Or = X | Y
// 型パラメータ
type Or<T, U> = T | U
データ型てきな
code:typescript
// なんかプリミティブ的なデータ型を表すやつ
type Prim =
| boolean // 最近はこの縦棒入れてる
| number
| string
| symbol
| null
| undefined;
type NonPrim = object;
// keyNameというプロパティにTがあるという型
type A<T> = {
keyName: T
}
// なんのキーも持ってないオブジェクト
type B = {}
type Op = {
x?: number
} //気持ち的には x は number | undefinedというより, xというキーが無いもの
const o: Op = {
x: undefined // ただしこれが通る
}
//配列はこんな感じ
type Arr = number[] // Array<number>
関数とか
code:typescript
// type Fun = function (r: number): string こうではなくて
type Fun = (r: number) => string //こう、rはなんでもいい
type Fun2 = {(r: number): string} // もしくはこう
// Indexed type
再帰的なやつ
code:typescript
interface List<T> { //interface 使えばかなりそれっぽい
0: T
1: null | List<T>
}
tsconfigはだいたいこれがおすすめ
code:json
{
"compilerOptions": {
"noImplicitReturns": true,
"preserveConstEnums": true,
"strict": true,
"declaration": true,
"diagnostics": true,
"target": "es2015"
},
"include": [
"src/**/*.ts"
]
}
サブタイプとは?アサイン可とは?
サブタイプってT ∋ S ?
ここに乗ってるのは結構ゆるい&古い,
strict: trueはここの扱いが変わるだけな気がする
サブタイプとアサイン可の違い
Spec
The assignment compatibility and subtyping rules differ only in that
the Any type is assignable to, but not a subtype of, all types,
the primitive type Number is assignable to, but not a subtype of, all enum types, and
an object type without a particular property is assignable to an object type in which that property is optional.
Handbook
Subtype vs Assignment
So far, we’ve used ‘compatible’, which is not a term defined in the language spec. In TypeScript, there are two kinds of compatibility: subtype and assignment. These differ only in that assignment extends subtype compatibility with rules to allow assignment to and from any and to and from enum with corresponding numeric values.
Different places in the language use one of the two compatibility mechanisms, depending on the situation. For practical purposes, type compatibility is dictated by assignment compatibility even in the cases of the implements and extends clauses. For more information, see the TypeScript spec.
大体T ∋ SならTにSはアサイン可能、たぶん。。。
const x: T = Sを満たすものって書ける
サブタイプ Intersection
{x: number; y:number}は{x: number}のサブタイプ?
code:typescript
type A = {
a: number
}
type B = {
b: number
}
var e: A = {a:4, b:5} // error
var ab: A & B = { a: 1, b: 1 };
var a: A = ab; // A & B assignable to A
var b: B = ab; // A & B assignable to B
S is an intersection type and at least one constituent type of S is a subtype of T.
サブタイプ Union
number | string | booleanはnumberのサブタイプ?
No
numberはnumber | string | booleanのサブタイプ
T is a union type and S is a subtype of at least one constituent type of T.
サブタイプ Literal
code:typescript
type Str = "a" | "b" | "c" // ∈ string
type Tru = true
// 内部的には type boolean = true | false
// true & boolean が true | (true & false)に展開されてたので
type = 42 // ∈ number
// Tuple
サブタイプ Function
(x: T1) => T2に(x: S1) => S2を代入したい
(x: S1) => S2 ∈ (x: T1) => T2を満たすためにはS1 ∋ T1 & S2 ∈ T2
直感的な範囲
code:typescript
type X = {
x: number
}
type Y = {
y: number
}
type T = (x: X & Y) => X
type S = (x: X) => X & Y // X ∋ X & Y だよ
function ng(f: T): S {
return f
}
function ok(f: S): T {
return f
}
2引数のときは??
code:typescript
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x
非直感的な入り口
code:typescript
type X = string
type Y = number
type T = (x: X) => X | Y
type S = (x: X | Y) => X // X | Y ∋ X だよ
function ng(f: T): S {
return f
}
function ok(f: S): T {
return f
}
非直感的な世界
code:typescript
type T = (x: X | Y) => (X | Y)
type S = (x: Y) => X
function ng(f: T): S {
return f
}
function ok(f: S): T {
return f
}
アサイン可能
code:typescript
type T = (x: X | Y) => X | Y
type S1 = ((x: X) => Y) | ((x: Y) => X)
type S2 =
| ((x: X) => X)
| ((x: Y) => X)
| ((x: X) => Y)
| ((x: Y) => Y)
type S3 = ((x: X | Y) => X) | ((x: X | Y) => Y)
互いにアサイン可
code:typescript
type X = {
x: number
}
type Y = {
y: number
}
type T = (x: X | Y) => X | Y
type S =
| ((x: X) => X)
| ((x: Y) => X)
| ((x: X) => Y)
| ((x: Y) => Y)
| ((x: X | Y) => X)
| ((x: X | Y) => Y)
| ((x: X) => X | Y)
| ((x: Y) => X | Y)
function ok1(f: T): S {
return f
}
function ok2(f: S): T {
return f
}
まとめ
怖い
パターンマッチ
Tagged Union
Control Flow Based Type Analysis
この2つで乗り切る
Tagged Union
code:typescript
type MyEvent =
| {
type: 'a'
data: number
}
| {
type: 'b'
data: string
}
CBF
code:typescript
function f(d: MyEvent) {
switch(d.type) {
case 'a':
//ここではd.dataはnumber
return
case 'b':
//こkではd.dataはstring
return
}
}
CBFを乗り切るために
code:typescript
typeof x === 'string'
function isT(x: T | U): x is T
if (isT(x)) {
// この中はT
}
swithc(true) { // 最終手段
case isT(x):
case isS(x):
case isU(x):
}
// 正直なるべくis関数がいるデータ構造にしない方が良い、それか自動生成
問題
code:typescript
function getTypeOf(f: number | string): 'string' | 'number'
答え
code:typescript
function getTypeOf(f: number | string): 'string' | 'number' {
if (typeof f === 'string') {
return 'string'
}
if (typeof f === "number") {
return 'number'
}
return f // 推論上neverになってるのに必要っぽい、不思議
}
//function getTypeOf(f) { return typeof f}で許してほしい
How implement Result 1
code:typescript
Result<T, E> = {
ok: true
data: T
} | {
ok: false
data: E
}
How implement Result 2
code:typescript
Result<T, E> = {
ok: true
} & T | {
ok: false
} & E
// T, Eともに okプロパティを持てないけど、こっちのほうが便利
// okの代わりにSymbol使っちゃう? -> うーん不便
Result使い所
async/awaitの型は結局Promise<T>なので、エラー状態によって処理を分けたければPromise<Result<T.E>>
too many connectionでX秒待たなければいけないエラーとか
throwしたらany、エラー型がどうでも良くなったらthrow
Curry化された関数の型
immutableな操作を行うmodify関数
code:typescript
function modify<T extends object, K extends keyof T>(key: K, f: (old: TK) => TK, struct: T): T { const newStruct = Object.assign({}, struct);
return newStruct;
}
modify('x', (n: number): number => n * 3, {x: 5})
curried
code:typescript
function modifyC<K extends string, T>(key: K, f: (old: T) => T): <U extends {P in K: T}>(x: U) => U {
return function <U extends {P in K: T}>(struct: U): U{ const newStruct = Object.assign({}, struct);
return newStruct;
}
}
modifyC('x', (c: number): number => c * 5) // <U extends {x: number}>(x:U) => U
最近やっていき
grpcをつかっている、protoファイルからの型生成
websocket上で json schema、json schemaから型生成
雑感、ゆるいサブタイプの型
code:typescript
//推論が落ち始めるのでどこに型を書くか
type N = {s: 'Inc', n: number}
const array: number[] = ...
const x = array.map(n => ({s: 'Inc', n})
// xの型はよく {s: string, n: number}
const x: N[] = array.map(n => ({s: 'Inc', n})//これは結構2.4っぽい書き型
const x = array.map<N>(n => ({s: 'Inc', n})//よくここに書く
//タプル推論落ちる
雑感、UnPartialの不思議
code:typescript
type Partial<T> = {
};
Partialの逆がしたい
結論から言うとできなかった、どの部分が?を持っているのか謎
雑感、tsfuck
code:typescript
true !== false // CE
雑感、Recordを楽するために使う
code:typescript
function h({x, y, z}: Record<'x'|'y'|'z', number>): number
// VNodeの時に便利
never落ち怖い
Nullable bind問題
Filter map問題
Typeにおけるinの扱い
Indexable type
Mapped type
引用してないところ(独自で書かれたところ)はLicense MIT