# Object Properties

{% tabs %}
{% tab title="📗 參考" %}
* TypeScript ⟩&#x20;
  * [Optional Properties](https://www.typescriptlang.org/docs/handbook/2/objects.html#optional-properties)
  * [Index Signatures](https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures)
{% endtab %}

{% tab title="Second Tab" %}

{% endtab %}
{% endtabs %}

## Optional

{% hint style="warning" %}
當宣告 **x?: T** 時，x 的型別實際上是 **T | undefined**
{% endhint %}

```typescript
// 
interface PaintOptions {
  x?: number;              // ⭐️ optional
  y?: number;
}

/*
interface Shape {
  paint(opts: PaintOptions = {});
}
*/

const shape = getShape();

shape.paint({ });
shape.paint({ x: 100 });
shape.paint({ y: 100 });
shape.paint({ x: 100, y: 100 });
```

## readonly

{% hint style="info" %}
**readonly** 與 **const** 的作用差不多，會保護 [Primitives](../primitives) 不受更改，但對於 [Object]() 來說，雖然**不能更改物件本身**，但**可更改**其**屬性**。
{% endhint %}

```typescript
interface SomeType {
  readonly prop: string;      // ⭐️ readonly
}
```

{% hint style="danger" %}
**readonly** properties **can be modified** via **aliasing**❗️
{% endhint %}

```typescript
interface Person {
  name: string;
  age : number;
}
 
interface ReadOnlyPerson {
  readonly name: string;      // ⭐️ readonly
  readonly age : number;      // ⭐️ readonly
}

// writable
let person: Person = {
  name: "Person McPersonface",
  age: 42,
};
 
// ⭐️ point to same person
let readonlyPerson: ReadOnlyPerson = person;
 
readonlyPerson.age;              // prints '42'
person.age++;                    // person is writable ⭐️
readonlyPerson.age;              // prints '43'
```

## Index Signatures

{% hint style="info" %}
TypeScript 的 [index signature](https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures) 類似於 Swift 的 [subscript](https://docs.swift.org/swift-book/LanguageGuide/Subscripts.html)。
{% endhint %}

```typescript
interface StringArray {
//╭─── ⭐️ ───╮
  [ index: T ]: string;       // ⭐️ index signature
}                             //    T = string or number
 
const a: StringArray = getStringArray();
const item = a[0];
```

{% hint style="warning" %}
An **index signature** property **type T** must be either ‘**string**’ or ‘**number**’.
{% endhint %}

{% hint style="danger" %}
It is possible to support both types of indexers, but the **type returned** from a **numeric indexer** must be a **subtype** of the **type returned** from the **string indexer**. This is because when **indexing** with a **number**, JavaScript will actually **convert** that to a **string** before indexing into an object. That means:

* **a\[100] === a\["100"]**
{% endhint %}
