簡単なgenerics(@TypeScript)

TypeScriptも静的型付け言語なのでgenericsが使えます。

以下はかんたんなサンプルコードですが、配列呼び出しにnumber[]とstring[]のケースです。

// generics
function comp<T>(arg: T[], comp: T): T[]{
    let ret: T[] = []

    for (let i = 0; i < arg.length; i++){
        if (arg[i] === comp){
        ret.push(arg[i])
        }
    }
    return ret
  }

console.log(comp([3, 2, 8, 10], 3))

console.log(comp(['3', '2', '8', '10'], '3'))

結果は想定通りに、

[ 3 ]
[ '3' ]

genericsはpolymorphism実現のための一つの手法なので、他にも実現手段はあります。

 

admin

関数の型定義と呼び出しシグネチャー(@TypeScript)

関数の型定義と呼び出しシグネチャーの例として、以下のrest parameterのコードを例にしてみます。

https://isehara-3lv.sakura.ne.jp/blog/2023/06/15/rest-parametertypescript/

// using call signature以下が呼び出しシグネチャーで書き換えてみたものですが、re_tに型情報をsrで与えているので、型のアノテーションが不要になりますから、型定義と関数の実態の分離といっても良さそうです。

// rest parameter (type safe compared to arguments method)
function sumRest(message: string, ...numbers: number[]):[string, number]{
    return [message, numbers.reduce((total, n) => total + n, 1)]
}

let res: [string, number] = sumRest('sum :', 1, 2, 3, 4, 5)
console.log(res[0], res[1] )

// using call signature
type sr = (message: string, ...numbers: number[]) => [string, number]
let re_t:sr = (message, ...numbers) => [message, numbers.reduce((total, n) => total + n, 1)]
console.log(re_t('call signature', 1, 2, 3, 4, 5))

実行結果:戻りは配列表記のままになってますが、

[ 'call signature', 16 ]

 

admin

rest parameter(@TypeScript)

関数に引数を渡す時に、不定長の場合の渡し方で配列を使った場合とrest parameterを使った場合です。

配列の場合はあくまで引数は配列が一個渡されるだけで、呼び出し側も配列で呼び出し。

rest parameterの場合には、呼び出し側は可変長引数を羅列ですから明らかに別物です。

// argument as an array
function sum(numbers: number[]): number{
    return numbers.reduce((total, n) => total + n, 1)       // the return value starts with 1
}

console.log(sum([1, 2, 3, 4, 5])) // 16

// rest parameter (type safe compared to arguments method)
function sumRest(message: string, ...numbers: number[]):[string, number]{
    return [message, numbers.reduce((total, n) => total + n, 1)]
}

let res: [string, number] = sumRest('sum :', 1, 2, 3, 4, 5)
console.log(res[0], res[1] ) // sum : 16

sumRest()の呼び出し時のresの戻り値型指定は不要ですがコメントのようなもの。sumRest()の定義側では戻り値の型指定は必須です。

 

admin

関数呼び出しのデフォルトパラメータ(@TypeScript)

関数のデフォルトパラメータを、以下でContextという形で定義した時に、関数で設定するときは以下のようなやり方。type指定で要素をオプション指定しているので、log関数では指定したい方だけ指定すれば良い。

// default parameter
type Context = {
    appID?: string,
    userID?: string
}

function log(message: string, context: Context = {userID:'aaabID'}){
    let time = new Date().toString()
    console.log(time, message, context.userID)
}

let id: Context = {userID: 'xxxyID'}
log('parameter', id)    // Wed Jun 14 2023 14:43:05 GMT+0900 (日本標準時) parameter xxxyID
log('parameter')        // Wed Jun 14 2023 14:43:05 GMT+0900 (日本標準時) parameter aaabID

TypeScriptというだけあって、型が中心にあるように感じる言語です。

 

admin

VScodeで識別子の定義が重複と言われる(@TypeScript)

VScodeでTypeScriptのコンパイルを行なって、ソースエディター上に.tsと.jsファイルを並べて表示させると、

こんなエラーが表示されます。コンパイルも実行も問題なくできるのですが、

対策はエディタ画面に同時表示させないこと、というだけですけれども。

ディレクトリの構成は、こんな感じです。

tsconfig.jsonは、

npx tsc --init

で作成。

% npx tsc --init

Created a new tsconfig.json with:                                               
                                                                             TS 
  target: es2016
  module: commonjs
  strict: true
  esModuleInterop: true
  skipLibCheck: true
  forceConsistentCasingInFileNames: true


You can learn more at https://aka.ms/tsconfig

 

admin

union & intersection of types(@TypeScript)

ちょっと見紛らわしい、union(論理和)とintersection(論理積)です。

// union & intersection of types
type Cat = {name: string, purrs: boolean}
type Dog = {name:string, barks: boolean}
type CatOrDog = Cat | Dog
type CatAndDog = Cat & Dog

let a_o: CatOrDog = {
    name: 'ball',   // need not to specify all properties
    barks: false
}

let a_a: CatAndDog = {
    name: "Dounald",
    barks: false,   // need to specify all properties of the Dog and Cat
    purrs: false
}

論理和(|)であるa_oはプロパティのいずれかが記述されてればいいけれども、論理積(&)であるa_aはプロパティを全て記述しないとエラーになります。

実際のコードではunionの方が遥かに登場機会が多いようですが、いずれかのプロパティを持てば良いという定義からしてそうなりそうです。

 

admin

{}, object, Objectの違い@TypeScript

TypeScriptはその名の通り、基本的に型を厳密に定義しないといけませんが、タイトルの通り似たような三つのオブジェクト定義が存在します。その性質の違いを見るのが以下のコードです。

// object, Object and {}
let o1: {}
let o2: object
let o3: Object  // Object is an interface

o1 = {a: 2} // object
o1 = 123    // primitive

o2 = {a: 2}
//o2 = 123  // primitive is not usable

o3 = {a: 2}
o3 = 123

o1 = {toString() {return 1}}    // accept any type of object
//o3 = {toString() {return 1}}    // treat o3 as a string type which is a predefined type

・{}, Object : オブジェクトもプリミティブも代入可能

・object : オブジェクトのみ代入可能

実はObjectはインターフェースであると、またTypeScriptではプリミティブ以外は全てオブジェクトです。

オブジェクトの定義方法としては、

オブジェクトとプリミティブの分離のためと思いますが、

let o0: {a: 1}のようなオブジェクトそのものを定義、もしくはobjectを使うのが推奨されています。

 

admin

type unknown@TypeScript

TypeScript読み始めて、型にanyがあるのは例えばGolangの構造体にもanyがあるから、まあそうかなと思いますがunknownというのは目新しい。

以下、unknown type説明用のコードですが、unknownでも限定的な演算子はサポートしていて例えば’===’はサポートされていますが、’+’演算子に対してはまさしくunknown扱いでコンパイルエラー(VScodeなので赤アンダーライン)になります。

typeofで型を特定してやればこの例ではnumberとして扱うことができています。

以下の実行結果で違いがわかります。

// unknown type 
let aa: unknown = 10    // type safe 'any'
let cc = aa === 10      // === is usable
console.log(cc)

//let dd = aa + 20      // -> cause error
if (typeof aa === 'number'){
    let dd = aa + 20
    console.log(dd)
}


// any type
let aaa: any = 10    // type safe 'any'
let ccc = aa === 10
console.log(ccc)

let ddd = aaa + 20
ddd = aaa + 20
console.log(ddd)

<results>

% ts-node "./hello.ts"
true
30
true
30

anyは文字通りどのような型であっても設定できますが、unknownは型の特定(annotation)が必要にされる場合があるところがanyとの違いです。故にunknownはtypesafe anyと呼ばれるようです。

この辺りの仕様は最近の言語仕様だと感じるところです。

P.S. 2023/6/13 unknownの補足

unknownは型の特定が必要ということで、もしunknownの変数を扱う場合には、例えばtypeofで型の絞り込みを行えば、TypeScriptがその型を認識してくれて処理ができます。以下、unknown型をnumber型として絞り込んで処理しています。

// type of unknown
let hh: unknown = 10
if (typeof hh === "number"){
    let hh_m = hh * 3
    console.log(hh_m)
}

 

admin

TypeScriptの概要本

TypeScriptについて一通り知るべき内容が記載されている書籍だろうと思います。

発行が2020年なのでTypeScript 3.0準拠でしょうが、4.0では大きな変更はないとのことなので差分を意識すれば十分使えるでしょう。

 

admin

 

micro:bitのカスタムブロックを作る

以下を参照に一番簡単に作れるであろう、出来合いの拡張ブロックを動かしてみた。

https://qiita.com/tkyko13/items/081b8759ed98f6ed577d

手順はリンクの通りですが、普通にGitHubに上げることもできるので共有(全体になるので、ブロックを部品としては扱えない)もできます。

内部的にはブロックはおそらくStatic TypeScriptに変換され、さらにJavaScript、そしてマシンコードに落とされてhexファイルが作成されて、それをmicro:bitに転送すれば実行されるという流れになります。

Type Script(Java Script)のコードは適当なドキュメントが存在しない(見つけられないだけ)ようなので、ブロックからJavaScriptのコードを拾い出して使うしかなさそう。

 

admin