「プログラミングTypeScript」を読む その1
オライリーの「プログラミングTypeScript」を読みながらのメモ
let x = {
a: 1,
b: "hello",
}
x = {
a: 2,
b: "world",
c: true, // Object literal may only specify known properties, and 'c' does not exist in type '{ a: number; b: string; }'.
}
上のコードはコンパイルエラーになるのに、なぜ下のコードはOKなのか?
let x = {
a: 1,
b: "hello",
}
type Foo = {
a: number;
b: string;
c: boolean;
}
const y: Foo = {
a: 2,
b: "world",
c: true,
}
x = y; // OK
これもOK
let x = {
a: 1,
b: "hello",
}
type Foo = {
a: number;
b: string;
c: boolean;
}
x = ({
a: 2,
b: "world",
c: true,
} as Foo);
console.log(x);
わからない。
let a: bigint = 10n;
console.log(a);
{
"compilerOptions": {
"lib": ["es2015", "dom"],
"module": "es2015",
"target": "es2020",
"strict": true,
},
}
bigintのリテラルを書くには tsconfig.json
の compilerOptions.target
を es2020
以上にする必要がある。 es2015
などにすると、 BigInt literals are not available when targeting lower than ES2020.
というコンパイルエラーが発生する。
もうひとつハマった点、 tsc hello.ts
とすると tsconfig.json
が反映されないみたい。 tsc
になにも引数を付けずに実行すれば tsconfig.json
の設定が反映された。
compilerOptions.lib
に es2020
以上を書けば、リテラルでなくても BigInt("10")
という表記ができる。
{
"compilerOptions": {
"lib": ["es2020", "dom"],
"module": "es2015",
"target": "es2020",
"strict": true
}
}
let a: bigint = BigInt("10");
console.log(a);
配列の型は string[]
と Array<string>
の2通りの書き方がある。意味はまったく同じ。前者はC言語っぽい見た目、後者はScalaっぽい見た目だから、後者のほうが好み。
void
とnever
があるのはScalaに似ている。
void
はUnit
、never
はNothing
。
JavaScriptと同じだが、関数の呼び出し方は4つある。
function hello(name: string) {
console.log(`Hello, ${name}!`);
}
hello("world");
hello.apply(null, ["world"]);
hello.call(null, "world");
hello.bind(null, "world")();
4つとも全部同じ。ただし関数内から参照できる this
の取り扱いが異なるらしい。
ジェネレータ
function* hello() {
let i = 0;
while (true) {
yield i++;
}
}
const generator = hello();
console.log(generator);
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
出力は
Object [Generator] {}
{ value: 0, done: false }
{ value: 1, done: false }
{ value: 2, done: false }
これもTypeScriptの機能ではなくJavaScriptの話らしい。
ジェネレータの型は Generator<number>
のように書く。
function* hello(): Generator<number> {
let i = 0;
while (true) {
yield i++;
}
}
ジェネレータの実行順序について深堀してみる。
function* hello(): Generator<number> {
let i = 0;
while (i < 2) {
console.log(`before yield ${i}`);
yield i;
console.log(`after yield ${i}`);
i++;
}
}
const generator = hello();
console.log(generator);
console.log("top level 0");
console.log(generator.next());
console.log("top level 1");
console.log(generator.next());
console.log("top level 2");
console.log(generator.next());
console.log("top level 3");
Object [Generator] {}
top level 0
before yield 0
{ value: 0, done: false }
top level 1
after yield 0
before yield 1
{ value: 1, done: false }
top level 2
after yield 1
{ value: undefined, done: true }
top level 3
やっぱり、最後の要素を取得した時点ではまだ最後かどうかを判定できず、次を取得しようとしたときにはじめてわかる。
関数の型の書き方
type Operator = (x: number, y: number) => number;
const op: Operator = function(x, y) {
return x + y;
}
console.log(op(1, 2)); // 3
本は途中だが、やっぱりJavaScriptの本を読むことにする。