阿里云主机折上折
  • 微信号
Current Site:Index > Index access type

Index access type

Author:Chuan Chen 阅读数:8833人阅读 分类: TypeScript

Basic Concepts of Indexed Access Types

Indexed Access Types allow retrieving the type of a property from another type through indexing. The syntax is T[K], where T is a type and K is a type that can serve as an index (typically a string literal type or a union type). This type query method is similar to accessing object property values via property names in JavaScript.

type Person = {
  name: string;
  age: number;
  address: {
    city: string;
    zipCode: string;
  };
};

type NameType = Person['name'];  // string
type AgeType = Person['age'];    // number
type AddressType = Person['address'];  // { city: string; zipCode: string; }

Indexed Access Types and Union Types

Indexed Access Types can be combined with union types to retrieve a union of multiple property types. When the index is a union type, the resulting type is a union of the corresponding property types.

type PersonProps = Person['name' | 'age'];  // string | number
type AllValues = Person[keyof Person];  
// string | number | { city: string; zipCode: string; }

Accessing Nested Properties

Indexed Access Types support accessing nested properties through chained indices to retrieve the types of deeply nested properties.

type CityType = Person['address']['city'];  // string
type ZipCodeType = Person['address']['zipCode'];  // string

Indexed Access for Arrays and Tuples

Indexed Access Types also apply to array and tuple types. For arrays, you can use number as the index type to retrieve the element type; for tuples, you can use numeric literal types to retrieve the type at a specific position.

type StringArray = string[];
type ElementType = StringArray[number];  // string

type Tuple = [string, number, boolean];
type First = Tuple[0];  // string
type Second = Tuple[1];  // number
type All = Tuple[number];  // string | number | boolean

Combining with Generics

Indexed Access Types are particularly useful in generics, enabling more flexible type operations. By constraining generic parameters, you can ensure the safety of indexed access.

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const person: Person = {
  name: 'Alice',
  age: 30,
  address: {
    city: 'New York',
    zipCode: '10001'
  }
};

const name = getProperty(person, 'name');  // string
const age = getProperty(person, 'age');    // number

Dynamic Property Access

Indexed Access Types can be used to dynamically compute property types, which is highly useful when working with complex type systems.

type DynamicAccess<T, K extends string> = K extends keyof T ? T[K] : never;

type Result1 = DynamicAccess<Person, 'name'>;  // string
type Result2 = DynamicAccess<Person, 'email'>;  // never

Combining with Mapped Types

Indexed Access Types are often used with mapped types to create new types based on existing ones.

type ReadonlyPerson = {
  readonly [K in keyof Person]: Person[K];
};

// Equivalent to
type ReadonlyPerson = {
  readonly name: string;
  readonly age: number;
  readonly address: {
    city: string;
    zipCode: string;
  };
};

Indexed Access in Conditional Types

Using Indexed Access Types in conditional types enables more complex type logic.

type NonFunctionPropertyNames<T> = {
  [K in keyof T]: T[K] extends Function ? never : K
}[keyof T];

type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;

interface MixedInterface {
  id: number;
  name: string;
  log: () => void;
  update: (data: any) => void;
}

type DataProps = NonFunctionProperties<MixedInterface>;
// { id: number; name: string }

Limitations of Indexed Access Types

Indexed Access Types have some limitations to note:

  1. The index must be a known property name or union type.
  2. You cannot access non-existent properties.
  3. Additional type guards are needed for dynamically computed property names.
// Error example
type Invalid1 = Person['invalid'];  // Error: Property 'invalid' does not exist on type 'Person'

// Requires type guarding
function safeAccess<T, K extends string>(obj: T, key: K): K extends keyof T ? T[K] : undefined {
  return (obj as any)[key];
}

Practical Use Cases

Indexed Access Types have various practical applications in real-world development:

  1. Type-safe property access functions
function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {
  return items.map(item => item[key]);
}

const people: Person[] = [
  { name: 'Alice', age: 30, address: { city: 'NY', zipCode: '10001' } },
  { name: 'Bob', age: 25, address: { city: 'LA', zipCode: '90001' } }
];

const names = pluck(people, 'name');  // string[]
const ages = pluck(people, 'age');    // number[]
  1. Extracting component Props types
interface ButtonProps {
  size: 'small' | 'medium' | 'large';
  variant: 'primary' | 'secondary';
  onClick: () => void;
  disabled?: boolean;
}

type ButtonSize = ButtonProps['size'];  // 'small' | 'medium' | 'large'
type ButtonVariant = ButtonProps['variant'];  // 'primary' | 'secondary'
  1. Handling API response types
interface ApiResponse<T> {
  data: T;
  status: number;
  error?: string;
}

type ExtractData<T> = T extends ApiResponse<infer U> ? U : never;

type UserResponse = ApiResponse<{ id: string; name: string }>;
type UserData = UserResponse['data'];  // { id: string; name: string }

Advanced Type Operations

Indexed Access Types can be used to build more advanced type manipulation tools:

  1. Deep property access
type DeepAccess<T, K extends string> = 
  K extends keyof T ? T[K] :
  K extends `${infer First}.${infer Rest}` ?
    First extends keyof T ? DeepAccess<T[First], Rest> :
    never :
  never;

type PersonCity = DeepAccess<Person, 'address.city'>;  // string
  1. Type-safe path access
type PathImpl<T, K extends keyof T> =
  K extends string ?
    T[K] extends Record<string, any> ?
      `${K}.${PathImpl<T[K], keyof T[K]>}` | K :
      K :
    never;

type Path<T> = PathImpl<T, keyof T>;

type PersonPath = Path<Person>; 
// "name" | "age" | "address" | "address.city" | "address.zipCode"
  1. Type retrieval based on paths
type PathValue<T, P extends Path<T>> =
  P extends `${infer K}.${infer Rest}` ?
    K extends keyof T ? PathValue<T[K], Rest & Path<T[K]>> : never :
    P extends keyof T ? T[P] : never;

type ZipCodeType = PathValue<Person, 'address.zipCode'>;  // string

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.