📝

TypeScriptとGoの基本文法をまとめてみた

2023/09/24に公開

変数

TypeScript

  • 変数宣言
let name: sring = "John"; // 再代入できる
const age: number = 30; // 再代入できない
  • 型推論

TypeScriptは型推論を行うため、変数の型宣言が必要な場合でも省略できることがある。

let message = "Hello, World!"; // 文字列型として推論
let count = 10;                // 数値型として推論

Go

  • 変数宣言
var name string = "John"
var age int = 30
  • 短縮変数宣言

:= 演算子を使用して、型を省略して変数を宣言し、値を初期化できる。ただし、これは関数内でのみ使用できる。

name := "John"
age := 30

TypeScript

  • 基本型
// 文字列型
let name: string = "John";     

// 数値型
let age: number = 30;        

// 真偽値型  
let isStudent: boolean = true; 
  • 配列の型
// 数値型の配列
let array: number[] = [1, 2, 3, 4, 5]; 
// Array<T> を使った書き方もできる
let array: Array<number> = [1, 2, 3, 4, 5];
  • オブジェクトの型
let person: { name: string, age: number } = { name: "John", age: 30 }  
  • 関数の型
function add(x: number, y: number): number {
    return x + y;
}

// 戻り値がない関数
function sayHello(): void {
    console.log("Hello, world!");
}
  • 列挙型
enum Color {
    Red,
    Blue,
    Yellow,
}
  • any型
let value: any;
value = 5;                // OK
value = "apple";          // OK
value = { name: "John" }; // OK
  • リテラル型
// 文字列リテラル型
type Fruit = "apple" | "banana" | "cherry";

const fruit: Fruit = "banana"; // 有効
// const fruit: Fruit = "grape"; // エラー: "grape"は許容されない値

// 数値リテラル型
type Digit = 0 | 1 | 2;

const digit: Digit = 2; // 有効
// const digit: Digit = 3; // エラー: 3は許容されない値
  • ユニオン型
// 数値または文字列のいずれかを持つ変数
let value: number | string;

value = 10;       // OK
value = "Hello";  // OK

// ユニオン型の配列
let array: (number | string)[] = [1, "two", 3, "four"];
  • インターセクション型
type StringOrNumber = string | number;
type NumberOrBoolean = number | boolean;
type BooleanOrSymbol = boolean | symbol;

// StringOrNumber型とNumberOrBoolean型に共通するnumber型と型推定される
type NumberType = StringOrNumber & NumberOrBoolean

// 共通する型が存在しない場合はnever型になる
type NeverType = StringOrNumber & BooleanOrSymbol;
  • 型アサーション
let anyVar: any = "Hello";

// 書き方は2つある
let strVar: string = anyVar as string;      //`as` 構文
let anotherStrVar: string = <string>anyVar; // アングルブラケット構文

// React のJSXと衝突する可能性があるため、JSXを使用するプロジェクトでは as 構文の使用が推奨されている

Go

  • 基本型の宣言
// 文字列型
var name string = "John" 

// 整数型
var age int = 30 

// 真偽値型
var isStudent bool = true
  • 配列とスライスの型
// 配列の型
var numbers [5]int = [5]int{1, 2, 3, 4, 5}

// スライスの型
var scores []int = int[]{90, 85, 78, 92}
  • 関数の型
func add(x int, y int) int {
    return x + y
}

// 戻り値のない関数は戻り値の型を指定する必要はない
func sayHello() {
    fmt.Println("Hello, world!")
}
  • 列挙型

列挙型(enum)は用意されていないが、iotaを利用することで列挙型に近い振る舞いを実現することが可能

type Color int

const (
    Red Color = iota
    Blue
    Yellow
)
  • any型
var value interface{}
value = 5       // OK
value = "apple" // OK
value = true    // OK

// Go 1.18以降はanyも使用可能
var v any
v = 2        // OK
v = "banana" // OK
  • 型アサーション
var i interface{} = "hello"
s := i.(string)

プリミティブ型の一覧

TypeScript

型名 説明
number 数値
string 文字列
boolean 真偽値
null null
undefined undefined
bigint number型よりも大きな整数
symbol ユニークなシンボル

JavaScriptにおけるデータ型は、プリミティブ型とオブジェクトの2つに分類されるため、これらプリミティブ型以外は、すべてオブジェクトと考えて問題ない

Go

型名 説明
bool 真偽値(trueまたはfalse)
int 符号付き整数
int8 8ビット符号付き整数
int16 16ビット符号付き整数
int32 32ビット符号付き整数
int64 64ビット符号付き整数
uint 符号なし整数
uint8 8ビット符号なし整数
uint16 16ビット符号なし整数
uint32 32ビット符号なし整数
uint64 64ビット符号なし整数
uintptr ポインタ値を格納する整数型
float32 32ビット浮動小数点数
float64 64ビット浮動小数点数
complex64 64ビット複素数型
complex128 128ビット複素数型
byte uint8のエイリアス
string 文字列
rune Unicodeのコードポイントを表すint32
error エラーを表すインターフェース

未定義の値

TypeScript

  • null

プログラムで意図的に値を欠落させる場合に使用

console.log(typeof(null)); // "object"
console.log(1 + null);     // 1(nullは0のように振る舞う)
  • undefined

変数が初期化されていない場合や関数に戻り値が指定されていない場合に自動的に設定される

console.log(typeof(undefined)); // "undefined"
console.log(1 + undefined);     // NaN
  • nullとundefinedを比較
console.log(null == undefined);  // true
console.log(null === undefined); // false

Go

  • nil
fmt.Println(reflect.TypeOf(nil)) // <nil>  

var ptr *int
fmt.Println(ptr)                 // <nil>
fmt.Println(reflect.TypeOf(ptr)) // *int

初期値

TypeScript

// 数値型 (number)
let num: number;
console.log(num); // undefined

// 文字列型 (string)
let str: string;
console.log(str); // undefined

// 真偽値型 (boolean)
let bool: boolean;
console.log(bool); // undefined

// 配列型 (Array)
let arr: number[] = [];
console.log(arr); // []

Go

// 整数型 (int)
var num int
fmt.Println(num) // 0

// 文字列型 (string)
var str string
fmt.Println(str) // ""

// 真偽値型 (bool)
var boolVar bool
fmt.Println(boolVar) // false

// 配列型 (array)
var arr [3]int
fmt.Println(arr) // [0 0 0]

// スライス
var slice []int
fmt.Println(slice) // []

// マップ
var m map[string]int
fmt.Println(m) // map[]

// ポインタ
var ptr *int
fmt.Println(ptr) // nil

// 構造体型 (struct)
type Person struct {
    Name string
    Age  int
}
var person Person
fmt.Println(person) // { 0}

値渡しと参照渡し

TypeScript

  • 値渡し

TypeScriptは基本的に値渡し

function valuePassing(num: number) {
    num = 10;         // numのコピーに対する変更
    console.log(num); // 10
}

let num = 5;

valuePassing(num);
console.log(num); // 5
  • 参照渡し

オブジェクトは参照渡しになる

function referencePassing(obj: any) {
    obj.property = 10;         // オブジェクトのプロパティを変更
}

let obj = { property: 5 };

referencePassing(obj);
console.log(obj.property); // 10

Go

  • 値渡し

Goは常に値渡し(オブジェクトも同様)

func valuePassing(num int) {
    num = 10 // numのコピーに対する変更
    fmt.Println(num) // 10
}

func main() {
    num := 5
    valuePassing(num)
    fmt.Println(num) // 5
}

  • 参照渡し

ポインタを使用して参照渡しができる

func referencePassing(ptr *int) {
    *ptr = 10 // ポインタを経由して変数を変更
}

func main() {
    num := 5
    referencePassing(&num)
    fmt.Println(num) // 10
}

文字列

TypeScript

  • 文字列結合
const str1: string = "Hello";
const str2: string = "World!";

console.log(str1 + "," + str2); // "Hello, World!"
  • joinメソッド
const words: string[] = ["Hello", "TypeScript", "World"];
const joined: string = words.join(" ");

console.log(joined); // "Hello TypeScript World"
  • テンプレートリテラル
const name = "Alice";
const age = 30;
const message = `Hello, my name is ${name} and I am ${age} years old.`;

console.log(message);

Go

  • 文字列結合
str1 := "Hello"
str2 := "World!"

result := str1 + ", " + str2
fmt.Println(result) // "Hello, World!"
  • strings.Join
words := []string{"Hello", "Go", "World!"}
joined := strings.Join(words, " ")

fmt.Println(joined) // "Hello Go World!"
  • strings.Builder
builder.WriteString("Hello ")
builder.WriteString("Go ")
builder.WriteString("World!")

result := builder.String()
fmt.Println(result) // "Hello Go World!"
  • fmt.Sprintf
name := "Bob"
age := 25

message := fmt.Sprintf("Hello, my name is %s and I am %d years old.", name, age)

fmt.Println(message)
verb
論理値(bool) %t
符号付き整数(int, int8など) %d
符号なし整数(uint, uint8など) %d
浮動小数点数(float64など) %g
複素数(complex128など) %g
文字列(string) %s
チャネル(chan) %p
ポインタ(pointer) %p

配列

TypeScript

  • 配列の宣言と初期化
// 数字の配列
let numbers: number[] = [1, 2, 3, 4, 5];

// 文字列の配列
let fruits: string[] = ["apple", "banana", "cherry"];

// 混合型の配列
let mixed: (string | number)[] = ["apple", 2, "cherry", 4];

// 初期化せずに空の配列を宣言
let emptyArray: number[] = [];
  • 配列の要素へのアクセス
// 配列の最初の要素にアクセス
let firstFruit: string = fruits[0]; 

// 配列の要素を変更
fruits[1] = "kiwi";          
  • 配列に値を追加
let numbers: number[] = [1, 2, 3, 4, 5];

// 配列の末尾に値を追加
numbers.push(6);

// 新しい値を含む新しい配列を作成
let nuwNumbers: number[] = [...numbers, 7, 8];
  • 配列の連結
const array1: number[] = [1, 2, 3];
const array2: number[] = [4, 5, 6];

const concatenatedArray: number[] = array1.concat(array2);
// スプレット演算子でも同じ結果を得ることができる
const combinedArray: number[] = [...array1, ...array2];
  • 配列の長さを取得
let numbers: number[] = [1, 2, 3, 4, 5];

let length: number = numbers.length;

Go

配列は固定長、スライスは可変長

  • 配列の宣言と初期化
// 整数型の配列 (固定サイズ)
var numbers [5]int = [5]int{1, 2, 3, 4, 5}

// 文字列型の配列 (固定サイズ)
fruitsArray := [3]string{"apple", "banana", "cherry"}

// 混合型の配列 (固定サイズ)
mixedArray := [4]interface{}{"apple", 2, "cherry", 4}

// 10つの整数からなる空の配列を宣言
var emptyArray [10]int
  • 配列の要素へのアクセス
//配列の最初の要素にアクセス
firstNumber := numbers[0] 

//配列の要素を変更
number[1] = 10       
  • スライスの宣言と初期化
// 整数型のスライス
var numbers []int = []int{1, 2, 3, 4, 5}

// 文字列型のスライス
fruits := []string{"apple", "banana", "cherry"}

// 混合型のスライス
mixed := []interface{}{"apple", 2, "cherry", 4}

// 空のスライスを宣言
var empty := []int
  • スライスの要素へのアクセス
// スライスの最初の要素にアクセス
firstFruit := fruits[0]  

// スライスの要素を変更
fruits[1] = "kiwi"      
  • スライスに値を追加
numbers := []int{1, 2, 3, 4, 5}

// スライスの末尾に値を追加
numbers = append(numbers, 6)

// 新しい値を含む新しいスライスを作成
newNumbers := append(numbers, 7, 8)
  • 配列の連結
slice1 := []int{1, 2, 3}
slice2 := []int{4, 5, 6}

concatenatedSlice := append(slice1, slice2...)
  • スライスの長さを取得
numbers := []int{1, 2, 3, 4, 5}

// スライスの長さを取得
length := len(numbers)

// スライスの確保済みメモリの容量を取得
capacity := cap(numbers)
  • make関数
// 整数型のスライスを作成し、長さを指定する
intSlice := make([]int, 5) // 長さ 5 の整数型スライスが作成され、要素はゼロ値 (0) で初期化される

// 文字列型のスライスを作成し、長さを指定する
stringSlice := make([]string, 3) // 長さ 3 の文字列型スライスが作成され、要素は空文字列で初期化される

// 混合型のスライスを作成し、容量を指定する
mixedSlice := make([]interface{}, 0, 10) // 長さ 0 で容量 10 の混合型スライスが作成される

連想配列

TypeScript

  • オブジェクトとして表現
const object: { [key: string]: number } = {};

object["John"] = 21;
object["Bob"] = 17;
object["Mike"] = 24;

console.log(object["Bob"]); // 17
  • オブジェクトからプロパティを削除
delete object["John"]; 

console.log(object["John"]); // undefined 

Go

  • map
m := make(map[string]int)
// m := make(map[string]int, 5) capを指定することもできる
m["John"] = 21
m["Bob"] = 17
m["Mike"] = 24

fmt.Println(m["Bob"]) // 17
  • mapから要素を削除
delete(m, "John")

fmt.Println(m["John"]) // 0

演算子

TypeScript

  • 算術演算子
let x: number = 10;
let y: number = 5;

let sum: number = x + y;        // 加算
let difference: number = x - y; // 減算
let product: number = x * y;    // 乗算
let quotient: number = x / y;   // 除算
let remainder: number = x % y;  // 剰余
  • 比較演算子
let a: number = 10;
let b: number = 5;

let isEqual: boolean = a === b;         // 等しい
let isNotEqual: boolean = a !== b;      // 等しくない
let isGreaterThan: boolean = a > b;     // より大きい
let isLessThan: boolean = a < b;        // より小さい
let isGreaterOrEqual: boolean = a >= b; // 以上
let isLessOrEqual: boolean = a <= b;    // 以下- 
  • 論理演算子
let p: boolean = true;
let q: boolean = false;

let logicalAnd: boolean = p && q; // 論理積 (AND)
let logicalOr: boolean = p || q;  // 論理和 (OR)
let logicalNot: boolean = !p;     // 論理否定 (NOT)
  • 三項演算子
const result = condition ? trueValue : falseValue;
  • 代入演算子
let x: number = 10;
let y: number = 5;

x += 3; // xは13 (x = x + 3)
y -= 2; // yは3 (y = y - 2)
x *= 2; // xは26 (x = x * 2)
y /= 4; // yは0.75 (y = y / 4)
x %= 5; // xは1 (x = x % 5)

Go

  • 算術演算子
x := 10
y := 5

sum := x + y        // 加算
difference := x - y // 減算
product := x * y    // 乗算
quotient := x / y   // 除算
remainder := x % y  // 剰余
  • 比較演算子
a := 10
b := 5

isEqual := a == b          // 等しい
isNotEqual := a != b       // 等しくない
isGreaterThan := a > b     // より大きい
isLessThan := a < b        // より小さい
isGreaterOrEqual := a >= b // 以上
isLessOrEqual := a <= b    // 以下
  • 論理演算子
p := true
q := false

logicalAnd := p && q // 論理積 (AND)
logicalOr := p || q  // 論理和 (OR)
logicalNot := !p     // 論理否定 (NOT)
  • 三項演算子

Goには三項演算子が存在しない

  • 代入演算子
x := 10
y := 5

x += 3 // xは13 (x = x + 3)
y -= 2 // yは3 (y = y - 2)
x *= 2 // xは26 (x = x * 2)
y /= 4 // yは0.75 (y = y / 4)
x %= 5 // xは1 (x = x % 5)

条件分岐

TypeScript

  • if
let condition: boolean = true;

if (condition) {
    // 条件が真の場合の処理
} else {
    // 条件が偽の場合の処理
}
  • else if
let score: number = 85;

if (score >= 90) {
    // 90以上の場合の処理
} else if (score >= 70) {
    // 70以上の場合の処理
} else {
    // それ以外の場合の処理
}
  • switch
let fruit: string = "apple";

switch (fruit) {
    case "apple":
        // "apple" の場合の処理
        break;
    case "banana":
        // "banana" の場合の処理
        break;
    default:
        // 上記の条件に該当しない場合の処理
}

Go

  • if
condition := true

if condition {
    // 条件が真の場合の処理
} else {
    // 条件が偽の場合の処理
}
  • else if
score := 85

if score >= 90 {
    // 90以上の場合の処理
} else if score >= 70 {
    // 70以上の場合の処理
} else {
    // 上記の条件に該当しない場合の処理
}
  • switch
fruit := "apple"

switch fruit {
    case "apple":
        // "apple" の場合の処理
    case "banana"
        // "banana" の場合の処理
    default:
        // 上記の条件に該当しない場合の処理
}
  • if文内での変数宣言
if v := calculate(); v > 10 {
    // calculate()の結果が10より大きい場合の処理
}

ループ

TypeScript

  • for
for (let i = 0; i < 5; i++) {
    // do something
}
  • while
let i = 0;
while (i < 5) {
    // do something
    i++;
}
  • do-while
let i = 0;
do {
    // do something
    i++;
} while (i < 5);
  • for-of
let fruits: string[] = ["apple", "banana", "cherry"];
for (let fruit of fruits) {
    // do something
}
  • for-in
const person = { name: "John", age: 30, city: "New York" };
for (let key in person) {
    console.log(`${key}: ${person[key]}`);
}

Go

  • for
for i := 0; i < 5; i++ {
    // do something
}
  • whileの代替
i := 0
for i < 5 {
    // do something
}
  • 無限ループ
for {
    // do something
    if someCondition {
        break // ループを抜ける
    }
}
  • range
// スライス
fruits := []string{"apple", "banana", "cherry"}
for _, fruit := range fruits {
    // do something
}

// map
person := map[string]interface{}{
    "name": "John",
    "age": 30,
    "city": "New York",
}

for key, value := range person {
    fmt.Printf("%s: %v\n", key, value)
}

関数

TypeScript

  • 関数の宣言
function greet(name: string): string {
    return `Hello, ${name}!`;
}
  • アロー関数
const add = (x: number, y: number): number => {
    return x + y;
}
  • デフォルト引数
function say(message: string, times: number = 1): void {
    for (let i = 0; i < times; i++) {
        console.log(message);
    }
}

Go

  • 関数宣言
func greet(name string) string {
    return fmt.Sprintf("Hello, %s!", name)
}
  • 無名関数
add := func(a, b int) int {
    return a + b
}
  • 即時関数

定義時に実行する

func(name string) {
    fmt.Printf("Hello, %s", name)
}("John") // Hello, John
  • 複数の戻り値
func divide(dividend, divisor int) (int, error) {
    if divisor == 0 {
        return 0, errors.New("division by zero")
    }
    return dividend / divisor, nil
}
  • defer

関数を抜ける際に実行される処理

func main() {
    defer fmt.Println("Good Bye!") // main関数を抜ける際に実行される
    fmt.Println("Hello!")
}
ターミナル
Hello
Good Bye

エラー

TypeScript

  • throw
function doSomething() { 
    if (condition) {
        throw new Error("An error occurred");
    }
}

Go

  • error型
func doSomething() error {
    if condition {
        return errors.New("An error occurred")
    }
    return nil
}
  • panic

プログラムを強制的に終了させる

func main() {
    if condition {
        panic("An panic occurred")
    }
    fmt.Println("Done") // panic関数が実行された場合、実行されない
}

recoverによりプログラムの中断を回復するすることができる

func runPanic() {
    // recoverはdeferの中で呼び出す
    defer func() {
        if e := recover(); e != nil {
            fmt.Println(e) // "An panic occurred"が出力される
        }
    }()
    panic("error")
    fmt.Println("done") // 実行されない
}

func main() {
    runPanic()
    fmt.Println("Done!") // 実行される
}

エラーハンドリング

TypeScript

  • try-catch
try {
    doSomething()
} catch (error) {
    console.log(error)
}

Go

  • error型の戻り値をif文でチェック
if err :=  doSomething(); err != nil {
    fmt.Println(err) 
}

モジュール

TypeScript

  • インポート
import useState from 'react';
import math from './math';
  • エクスポート
math.ts
export function add(a, b) {
    return a + b;
}
  • インストール
npm install <package> // npmの場合
yarn add <package>    // yarnの場合
pnpm add <package>    // pnpmの場合
bun add <package>     // bunの場合
  • 削除
npm uninstall <package> // npmの場合
yarn remove <package>   // yarnの場合
pnpm remove <package>   // pnpmの場合
bun remove <package>    // bunの場合

Go

  • インポート
import "fmt"
import "math"
  • エクスポート
package math

func add() {
    return a + b
}
  • インストール
go install <package> 

# ソースコード内のimportで指定されているモジュールをインストールしてくれる
go mod tidy
  • 削除
go clean -i <package>

# ソースコード内のimportで指定されていないモジュールを削除してくれる
go mod tidy

非同期処理

TypeScript

  • async / await
async function fetchData(): Promise<any> {
    const data = await fetch('https://example.com/data');
    return data.json();
}

async function main() {
    const result = await fetchData();
    console.log(result);
}

main();
  • Promise.all
const timeout = async (time: number): Promise<number> => {
    console.log(`Start ${time}`);
    await new Promise((res) => setTimeout(res, time));
    console.log(`Done ${time}`);
    return time;
}

Promise.all([
    timeout(300),
    timeout(200),
    timeout(100),
]).then((res) => console.log(res));

すべての処理が完了してから、それぞれの結果を配列で取得することができる

ターミナル
Start 300
Start 200
Start 100
Done 100
Done 200
Done 300
[ 300, 200, 100 ]

Go

  • goroutine

Goのランタイムで管理されている非常に軽量で効率的なスレッドであるgoroutineを使って、並行処理をサポートする

func say(s string) {
    for i := 0; i < 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go say("world")
    say("hello")
}

実行するたびに異なる結果になる

ターミナル
World
Hello
Hello
World
Hello
World
World
Hello
Hello
World
  • channel

goroutineに対してデータを送受信できる

func sendMessage(ch chan string, message string) {
    ch <- message // チャネルにデータを送信
}

func main() {
    messageChannel := make(chan string) // 文字列を送受信するチャネルを作成

    go sendMessage(messageChannel, "Hello")

    receivedMessage := <-messageChannel // チャネルからデータを受信
    fmt.Println(receivedMessage)

    time.Sleep(1 * time.Second)
}
ターミナル
Hello
  • sync.WaitGroup

goroutine の実行を待つ事ができる

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
	time.Sleep(2 * time.Second)
	fmt.Println("Done")
    }()
    
    wg.Wait()
}

クラス / 構造体

TypeScript

  • クラスとオブジェクト

クラスを使用してオブジェクトを定義できる。

class Person {
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    greet() {
        console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
    }
}

const person1 = new Person("Alice", 30);
person1.greet();
  • 継承

クラスの継承をサポート。

class Student extends Person {
    constructor(name: string, age: number, studentId: string) {
        super(name, age);
        this.studentId = studentId;
    }

    study() {
        console.log(`${this.name} is studying.`);
    }
}

const student1 = new Student("Bob", 25, "12345");
student1.greet();
student1.study();
  • インターフェース

インターフェースを使用してクラスやオブジェクトの形状を定義できる

interface Animal {
    name: string;
    makeSound(): void;
}

class Dog implements Animal {
    name: string;

    constructor(name: string) {
        this.name = name;
    }

    makeSound() {
        console.log(`${this.name} barks!`);
    }
}

const myDog = new Dog("Buddy");
myDog.makeSound();
  • カプセル化

アクセス修飾子を使用してカプセル化を制御する。アクセス修飾子には次の3つがある。

public デフォルトのアクセス修飾子で、どこからでもアクセスできる。
private クラス内からのみアクセスできる。
protected クラス内とサブクラス内からのみアクセスできる。

class BankAccount {
    private balance: number = 0;

    constructor(initialBalance: number) {
        this.balance = initialBalance;
    }

    deposit(amount: number) {
        if (amount > 0) {
            this.balance += amount;
        }
    }

    withdraw(amount: number) {
        if (amount > 0 && amount <= this.balance) {
            this.balance -= amount;
        }
    }

    getBalance() {
        return this.balance;
    }
}

const account = new BankAccount(1000);
account.deposit(500);
// アクセス修飾子がprivateなので、直接balanceにアクセスはできない
// console.log(`Account balance: ${account.balance}`);
console.log(`Account balance: ${account.getBalance()}`);
balanceプロパティはprivateで宣言されているため、外部から直接アクセスできず、getBalanceメソッドを使用してアクセスしている。

Go

  • 構造体とメソッド

構造体とメソッドを使用する

package main

import (
    "fmt"
)

type Person struct {
    Name string
    Age  int
}

func (p Person) Greet() {
    fmt.Printf("Hello, my name is %s and I am %d years old.\n", p.Name, p.Age)
}

func main() {
    person1 := Person{"Alice", 30}
    person1.Greet()
}
  • インターフェース

interfaceの中にある関数名と同一の関数が全て実装されている構造体に自動的にインターフェースが実装されるためimplementsが必要ない

package main

import (
    "fmt"
)

type Animal interface {
    MakeSound()
}

type Dog struct {
    Name string
}

func (d Dog) MakeSound() {
    fmt.Printf("%s barks!\n", d.Name)
}

func main() {
    myDog := Dog{Name: "Buddy"}
    var animal Animal = myDog
    animal.MakeSound()
}
  • カプセル化

具体的なアクセス修飾子は存在せず、識別子の大文字と小文字を使用して可視性を制御する。
大文字で始まる識別子は他のパッケージからもアクセス可能で、小文字で始まる識別子はパッケージ内でのみアクセス可能。

package main

import (
    "fmt"
)

type BankAccount struct {
    balance float64
}

func NewBankAccount(initialBalance float64) *BankAccount {
    return &BankAccount{balance: initialBalance}
}

func (a *BankAccount) Deposit(amount float64) {
    if amount > 0 {
        a.balance += amount
    }
}

func (a *BankAccount) Withdraw(amount float64) {
    if amount > 0 && amount <= a.balance {
        a.balance -= amount
    }
}

func (a *BankAccount) GetBalance() float64 {
    return a.balance
}

func main() {
    account := NewBankAccount(1000)
    account.Deposit(500)
    // balanceは小文字で始まるため、他のパッケージからはアクセスできない
    // fmt.Printf("Account balance: %.2f\n", account.balance)
    fmt.Printf("Account balance: %.2f\n", account.GetBalance())
}
  • ファクトリー関数

コンストラクタが提供されないため、新しいオブジェクトを初期化するための関数を自分で定義することが一般的。関数名をNewから始める慣習がある。

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func NewPerson(name string, age int) *Person {
    return &Person{
        Name: name,
        Age:  age,
    }
}

func main() {
    // NewPerson関数を使用してPersonオブジェクトを初期化
    person := NewPerson("Bob", 25)

    fmt.Printf("Name: %s, Age: %d\n", person.Name, person.Age)
}

ジェネリクス

TypeScript

  • 関数のジェネリクス
function identity<T>(value: T): T {
    return value;
}

// 使用例
let result = identity<string>("Hello, TypeScript!"); // 型引数を指定
console.log(result); // "Hello, TypeScript!"

// 型引数を省略することも可能
let result2 = identity(42);
console.log(result2); // 42
  • インターフェースのジェネリクス
interface Box<T> {
    value: T;
}

// 使用例
let box1: Box<string> = { value: "TypeScript" };
let box2: Box<number> = { value: 42 };

Go

  • 関数のジェネリクス
func message[T any](value T) T {
    return value
}

// 使用例
func main() {
    result := message("Hello, Go!")
    fmt.Println(result) // "Hello, Go!"
}
  • スライスのジェネリクス
func Filter[T any](s []T, predicate func(T) bool) []T {
    var result []T
    for _, item := range s {
        if predicate(item) {
            result = append(result, item)
        }
    }
    return result
}

// 使用例
func main() {
    numbers := []int{1, 2, 3, 4, 5}
    filtered := Filter(numbers, func(n int) bool {
        return n % 2 == 0
    })
    fmt.Println(filtered) // [2 4]
}

Discussion