面试官:说说泛型和类型体操?

78 阅读9分钟

泛型和类型体操

泛型和类型体操(Type Gymnastics)是 TypeScript 中高级类型系统的重要组成部分。它们提供了强大的工具和技巧,用于处理复杂的类型操作和转换。

泛型(Generics)

1. 泛型函数

泛型函数允许我们在函数定义中使用类型参数,以便在函数调用时动态指定类型。例如:

                    let result = identity<number>(42);  // result 的类型为 number" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript"><span class="hljs-keyword">function</span> identity&lt;T&gt;(<span class="hljs-attr">arg</span>: T): T {
        <span class="hljs-keyword">return</span> arg;
        }
        
        <span class="hljs-keyword">let</span> result = identity&lt;<span class="hljs-built_in">number</span>&gt;(<span class="hljs-number">42</span>);  <span class="hljs-comment">// result 的类型为 number</span></pre><p>在上面的示例中,<code>identity</code> 函数使用类型参数 <code>T</code>,并返回与输入类型相同的值。通过显式传递泛型参数,我们可以确保在函数调用时指定了具体的类型。</p><h4>2. 泛型接口</h4><p>泛型接口允许我们在接口定义中使用类型参数,以便在实现该接口时指定具体的类型。例如:</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="interface Container<T> {
                    value: T;
                    }
                    
                    let container: Container<number> = { value: 42 };" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">Container</span>&lt;T&gt; {
        <span class="hljs-attr">value</span>: T;
        }
        
        <span class="hljs-keyword">let</span> <span class="hljs-attr">container</span>: <span class="hljs-title class_">Container</span>&lt;<span class="hljs-built_in">number</span>&gt; = { <span class="hljs-attr">value</span>: <span class="hljs-number">42</span> };</pre><p>在上面的示例中,我们定义了一个泛型接口 <code>Container</code>,它包含一个类型参数 <code>T</code>。通过指定 <code>Container&lt;number&gt;</code>,我们创建了一个具体的实现,其中的 <code>value</code> 属性类型为 <code>number</code></p><h4>3. 泛型类</h4><p>泛型类允许我们在类定义中使用类型参数,以便在创建类的实例时指定具体的类型。例如:</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="class Stack<T> {
                    private items: T[] = [];
                    
                      push(item: T) {
                          this.items.push(item);
                            }
                            
                              pop(): T | undefined {
                                  return this.items.pop();
                                    }
                                    }
                                    
                                    let stack = new Stack<number>();
                                    stack.push(1);
                                    stack.push(2);
                                    let item = stack.pop();  // item 的类型为 number | undefined" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Stack</span>&lt;T&gt; {
        <span class="hljs-keyword">private</span> <span class="hljs-attr">items</span>: T[] = [];
        
          <span class="hljs-title function_">push</span>(<span class="hljs-params">item: T</span>) {
              <span class="hljs-variable language_">this</span>.<span class="hljs-property">items</span>.<span class="hljs-title function_">push</span>(item);
                }
                
                  <span class="hljs-title function_">pop</span>(): T | <span class="hljs-literal">undefined</span> {
                      <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">items</span>.<span class="hljs-title function_">pop</span>();
                        }
                        }
                        
                        <span class="hljs-keyword">let</span> stack = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Stack</span>&lt;<span class="hljs-built_in">number</span>&gt;();
                        stack.<span class="hljs-title function_">push</span>(<span class="hljs-number">1</span>);
                        stack.<span class="hljs-title function_">push</span>(<span class="hljs-number">2</span>);
                        <span class="hljs-keyword">let</span> item = stack.<span class="hljs-title function_">pop</span>();  <span class="hljs-comment">// item 的类型为 number | undefined</span></pre><p>在上面的示例中,我们定义了一个泛型类 <code>Stack</code>,它使用类型参数 <code>T</code> 来表示堆栈中的元素类型。通过创建 <code>Stack&lt;number&gt;</code> 的实例,我们限制了堆栈中的元素必须为 <code>number</code> 类型。</p><h3 id="item-1-2">类型体操(Type Gymnastics)</h3><h4>1. 条件类型(Conditional Types)</h4><p>条件类型允许我们根据输入类型的条件判断结果来选择不同的类型。条件类型的语法形式为:</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="T extends U ? X : Y" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript">T <span class="hljs-keyword">extends</span> U ? X : Y</pre><p>其中,<code>T</code> 是待检查的类型,<code>U</code> 是条件类型,<code>X</code> 是满足条件时返回的类型,<code>Y</code> 是不满足条件时返回的类型。</p><p>下面是一个使用条件类型的示例:</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="type Check<T> = T extends string ? true : false;
                  
                  type Result = Check<string>;  // Result 的类型为 true" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript"><span class="hljs-keyword">type</span> <span class="hljs-title class_">Check</span>&lt;T&gt; = T <span class="hljs-keyword">extends</span> <span class="hljs-built_in">string</span> ? <span class="hljs-literal">true</span> : <span class="hljs-literal">false</span>;
      
      <span class="hljs-keyword">type</span> <span class="hljs-title class_">Result</span> = <span class="hljs-title class_">Check</span>&lt;<span class="hljs-built_in">string</span>&gt;;  <span class="hljs-comment">// Result 的类型为 true</span></pre><p>在上面的示例中,我们定义了一个条件</p><p>类型 <code>Check&lt;T&gt;</code>,它接受一个类型参数 <code>T</code>。如果 <code>T</code><code>string</code> 类型,那么 <code>Check&lt;T&gt;</code> 的类型将是 <code>true</code>,否则为 <code>false</code></p><h4>2. <code>keyof</code> 操作符和索引访问类型</h4><p><code>keyof</code> 操作符用于获取类型的所有属性名,结合索引访问类型可以从一个类型中获取属性的具体类型。</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="interface Person {
                    name: string;
                      age: number;
                      }
                      
                      type PersonKeys = keyof Person;  // &quot;name&quot; | &quot;age&quot;
                      type PersonNameType = Person['name'];  // string" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">Person</span> {
        <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;
          <span class="hljs-attr">age</span>: <span class="hljs-built_in">number</span>;
          }
          
          <span class="hljs-keyword">type</span> <span class="hljs-title class_">PersonKeys</span> = keyof <span class="hljs-title class_">Person</span>;  <span class="hljs-comment">// "name" | "age"</span>
          <span class="hljs-keyword">type</span> <span class="hljs-title class_">PersonNameType</span> = <span class="hljs-title class_">Person</span>[<span class="hljs-string">'name'</span>];  <span class="hljs-comment">// string</span></pre><p>在上面的示例中,我们使用 <code>keyof</code> 操作符获取了 <code>Person</code> 接口的属性名集合,并通过索引访问类型获取了 <code>Person</code> 接口中 <code>name</code> 属性的类型。</p><h4>3. <code>infer</code> 关键字</h4><p><code>infer</code> 关键字用于在条件类型中推断类型,并将其赋值给一个类型变量。</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
                  
                  function add(a: number, b: number): number {
                    return a + b;
                    }
                    
                    type AddReturnValue = ReturnType<typeof add>;  // 类型为 number" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript"><span class="hljs-keyword">type</span> <span class="hljs-title class_">ReturnType</span>&lt;T&gt; = T <span class="hljs-keyword">extends</span> (...<span class="hljs-attr">args</span>: <span class="hljs-built_in">any</span>[]) =&gt; infer R ? R : <span class="hljs-built_in">never</span>;
      
      <span class="hljs-keyword">function</span> <span class="hljs-title function_">add</span>(<span class="hljs-params">a: <span class="hljs-built_in">number</span>, b: <span class="hljs-built_in">number</span></span>): <span class="hljs-built_in">number</span> {
        <span class="hljs-keyword">return</span> a + b;
        }
        
        <span class="hljs-keyword">type</span> <span class="hljs-title class_">AddReturnValue</span> = <span class="hljs-title class_">ReturnType</span>&lt;<span class="hljs-keyword">typeof</span> add&gt;;  <span class="hljs-comment">// 类型为 number</span></pre><p>在上面的示例中,<code>ReturnType</code> 类型接受一个类型参数 <code>T</code>,并使用条件类型和 <code>infer</code> 关键字推断函数类型的返回类型。通过调用 <code>ReturnType&lt;typeof add&gt;</code>,我们推断出 <code>add</code> 函数的返回类型为 <code>number</code></p><p>当涉及到泛型时,还有一些重要的概念和内置泛型函数可以深入分析。让我们继续探讨 <code>extends</code> 关键字、TS 官方内置的一些泛型函数以及它们的使用。</p><h3 id="item-1-3"><code>extends</code> 关键字和类型约束</h3><p>在泛型中,我们可以使用 <code>extends</code> 关键字来对泛型类型进行约束。这样可以确保传递给泛型的类型满足特定条件。</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="function printProperty<T extends { name: string }>(obj: T): void {
                    console.log(obj.name);
                    }
                    
                    printProperty({ name: 'John', age: 25 });  // 输出 'John'" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript"><span class="hljs-keyword">function</span> printProperty&lt;T <span class="hljs-keyword">extends</span> { <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span> }&gt;(<span class="hljs-attr">obj</span>: T): <span class="hljs-built_in">void</span> {
        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(obj.<span class="hljs-property">name</span>);
        }
        
        <span class="hljs-title function_">printProperty</span>({ <span class="hljs-attr">name</span>: <span class="hljs-string">'John'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">25</span> });  <span class="hljs-comment">// 输出 'John'</span></pre><p>在上面的示例中,<code>printProperty</code> 函数接受一个泛型参数 <code>T</code>,该参数必须满足一个约束条件:具有 <code>name</code> 属性,且 <code>name</code> 的类型为 <code>string</code>。通过使用 <code>extends</code> 关键字和类型约束,我们可以确保 <code>obj</code> 参数具有所需的属性和类型,从而避免出现错误。</p><h4>泛型函数Util</h4><p>TypeScript 提供了一些内置的泛型函数,这些函数被广泛用于处理各种类型操作。以下是一些常见的官方内置泛型函数:</p><h5><code>Partial&lt;T&gt;</code></h5><p><code>Partial&lt;T&gt;</code> 是 TypeScript 中的一个内置泛型类型,它可以将给定类型 <code>T</code> 中的所有属性转换为可选属性。这对于创建部分完整的对象非常有用。</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="interface Person {
                    name: string;
                      age: number;
                      }
                      
                      type PartialPerson = Partial<Person>;
                      
                      const partialPerson: PartialPerson = { name: 'John' };  // age 属性是可选的" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">Person</span> {
        <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;
          <span class="hljs-attr">age</span>: <span class="hljs-built_in">number</span>;
          }
          
          <span class="hljs-keyword">type</span> <span class="hljs-title class_">PartialPerson</span> = <span class="hljs-title class_">Partial</span>&lt;<span class="hljs-title class_">Person</span>&gt;;
          
          <span class="hljs-keyword">const</span> <span class="hljs-attr">partialPerson</span>: <span class="hljs-title class_">PartialPerson</span> = { <span class="hljs-attr">name</span>: <span class="hljs-string">'John'</span> };  <span class="hljs-comment">// age 属性是可选的</span></pre><p>在上面的示例中,<code>Partial&lt;Person&gt;</code><code>Person</code> 接口中的所有属性变为可选属性,从而创建了一个部分完整的 <code>PartialPerson</code> 类型。</p><h5><code>Required&lt;T&gt;</code></h5><p><code>Required&lt;T&gt;</code> 是 TypeScript 中的另一个内置泛型类型,它可以将给定类型 <code>T</code> 中的所有可选属性转换为必需属性。这对于确保对象的完整性非常有用。</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="interface Person {
                    name?: string;
                      age?: number;
                      }
                      
                      type RequiredPerson = Required<Person>;
                      
                      const requiredPerson: RequiredPerson = { name: 'John', age: 25 };  // name 和 age 属性是必需的" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">Person</span> {
        name?: <span class="hljs-built_in">string</span>;
          age?: <span class="hljs-built_in">number</span>;
          }
          
          <span class="hljs-keyword">type</span> <span class="hljs-title class_">RequiredPerson</span> = <span class="hljs-title class_">Required</span>&lt;<span class="hljs-title class_">Person</span>&gt;;
          
          <span class="hljs-keyword">const</span> <span class="hljs-attr">requiredPerson</span>: <span class="hljs-title class_">RequiredPerson</span> = { <span class="hljs-attr">name</span>: <span class="hljs-string">'John'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">25</span> };  <span class="hljs-comment">// name 和 age 属性是必需的</span></pre><p>在上面的示例中,<code>Required&lt;Person&gt;</code><code>Person</code> 接口中的所有可选属性变为必需属性,从而创建了一个要求完整性的 <code>RequiredPerson</code> 类型。</p><h5><code>Pick&lt;T, K&gt;</code></h5><p><code>Pick&lt;T, K&gt;</code> 是 TypeScript 中的另一个内置泛型函数,它可以从给定类型 <code>T</code> 中选择指定的属性 <code>K</code> 组成一个新的类型。</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="interface Person {
                    name: string;
                      age: number;
                        address: string;
                        }
                        
                        type NameAndAge = Pick<Person, 'name' | 'age'>;
                        
                        const person: NameAndAge = { name:
                        
                         'John', age: 25 };  // 只包含 name 和 age 属性" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">Person</span> {
        <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;
          <span class="hljs-attr">age</span>: <span class="hljs-built_in">number</span>;
            <span class="hljs-attr">address</span>: <span class="hljs-built_in">string</span>;
            }
            
            <span class="hljs-keyword">type</span> <span class="hljs-title class_">NameAndAge</span> = <span class="hljs-title class_">Pick</span>&lt;<span class="hljs-title class_">Person</span>, <span class="hljs-string">'name'</span> | <span class="hljs-string">'age'</span>&gt;;
            
            <span class="hljs-keyword">const</span> <span class="hljs-attr">person</span>: <span class="hljs-title class_">NameAndAge</span> = { <span class="hljs-attr">name</span>:
            
             <span class="hljs-string">'John'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">25</span> };  <span class="hljs-comment">// 只包含 name 和 age 属性</span></pre><p>在上面的示例中,<code>Pick&lt;Person, 'name' | 'age'&gt;</code><code>Person</code> 接口中选择了 <code>'name'</code><code>'age'</code> 属性,创建了一个新的类型 <code>NameAndAge</code></p><p>我们还可以结合泛型和内置泛型函数来实现更复杂的类型操作。以下是一个示例,展示了如何使用 <code>Pick</code> 和泛型来创建一个函数,该函数从给定对象中选择指定属性,并返回一个新的对象。</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="function pickProperties<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
                    const result: Partial<T> = {};
                      for (const key of keys) {
                          result[key] = obj[key];
                            }
                              return result as Pick<T, K>;
                              }
                              
                              interface Person {
                                name: string;
                                  age: number;
                                    address: string;
                                    }
                                    
                                    const person: Person = {
                                      name: 'John',
                                        age: 25,
                                          address: '123 Main St'
                                          };
                                          
                                          const nameAndAge = pickProperties(person, ['name', 'age']);  // 只包含 name 和 age 属性
                                          console.log(nameAndAge);  // 输出: { name: 'John', age: 25 }" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript"><span class="hljs-keyword">function</span> pickProperties&lt;T, K <span class="hljs-keyword">extends</span> keyof T&gt;(<span class="hljs-attr">obj</span>: T, <span class="hljs-attr">keys</span>: K[]): <span class="hljs-title class_">Pick</span>&lt;T, K&gt; {
        <span class="hljs-keyword">const</span> <span class="hljs-attr">result</span>: <span class="hljs-title class_">Partial</span>&lt;T&gt; = {};
          <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> key <span class="hljs-keyword">of</span> keys) {
              result[key] = obj[key];
                }
                  <span class="hljs-keyword">return</span> result <span class="hljs-keyword">as</span> <span class="hljs-title class_">Pick</span>&lt;T, K&gt;;
                  }
                  
                  <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Person</span> {
                    <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;
                      <span class="hljs-attr">age</span>: <span class="hljs-built_in">number</span>;
                        <span class="hljs-attr">address</span>: <span class="hljs-built_in">string</span>;
                        }
                        
                        <span class="hljs-keyword">const</span> <span class="hljs-attr">person</span>: <span class="hljs-title class_">Person</span> = {
                          <span class="hljs-attr">name</span>: <span class="hljs-string">'John'</span>,
                            <span class="hljs-attr">age</span>: <span class="hljs-number">25</span>,
                              <span class="hljs-attr">address</span>: <span class="hljs-string">'123 Main St'</span>
                              };
                              
                              <span class="hljs-keyword">const</span> nameAndAge = <span class="hljs-title function_">pickProperties</span>(person, [<span class="hljs-string">'name'</span>, <span class="hljs-string">'age'</span>]);  <span class="hljs-comment">// 只包含 name 和 age 属性</span>
                              <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(nameAndAge);  <span class="hljs-comment">// 输出: { name: 'John', age: 25 }</span></pre><p>在上面的示例中,<code>pickProperties</code> 函数接受一个泛型参数 <code>T</code> 和一个属性数组 <code>keys</code>。通过使用 <code>Pick&lt;T, K&gt;</code>,我们将从给定对象 <code>obj</code> 中选择指定的属性 <code>keys</code>,并创建一个新的对象。</p><p>这个例子结合了泛型、内置泛型函数 <code>Pick</code><code>keyof</code> 操作符和 <code>extends</code> 关键字,展示了如何在 TypeScript 中处理复杂的类型操作和转换。</p><p>当涉及到官方内置的泛型函数时,还有一些重要的函数值得分析。让我们继续探讨一些常用的官方内置泛型函数以及它们的使用。</p><h4><code>Exclude&lt;T, U&gt;</code></h4><p><code>Exclude&lt;T, U&gt;</code> 是 TypeScript 中的一个内置泛型函数,用于从类型 <code>T</code> 中排除类型 <code>U</code>。它返回一个新类型,该新类型包含在 <code>T</code> 中存在但不在 <code>U</code> 中存在的成员类型。</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="type T = Exclude<&quot;a&quot; | &quot;b&quot; | &quot;c&quot;, &quot;a&quot; | &quot;b&quot;>;  // T 的类型为 &quot;c&quot;" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript"><span class="hljs-keyword">type</span> T = <span class="hljs-title class_">Exclude</span>&lt;<span class="hljs-string">"a"</span> | <span class="hljs-string">"b"</span> | <span class="hljs-string">"c"</span>, <span class="hljs-string">"a"</span> | <span class="hljs-string">"b"</span>&gt;;  <span class="hljs-comment">// T 的类型为 "c"</span></pre><p>在上面的示例中,<code>Exclude&lt;"a" | "b" | "c", "a" | "b"&gt;</code> 排除了类型 <code>"a"</code><code>"b"</code>,返回类型为 <code>"c"</code></p><h4><code>Omit&lt;T, K&gt;</code></h4><p><code>Omit&lt;T, K&gt;</code> 是 TypeScript 中的另一个内置泛型函数,它返回一个新类型,该新类型排除了类型 <code>T</code> 中指定的属性 <code>K</code></p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="interface Person {
                    name: string;
                      age: number;
                        address: string;
                        }
                        
                        type PersonWithoutAddress = Omit<Person, &quot;address&quot;>;" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">Person</span> {
        <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;
          <span class="hljs-attr">age</span>: <span class="hljs-built_in">number</span>;
            <span class="hljs-attr">address</span>: <span class="hljs-built_in">string</span>;
            }
            
            <span class="hljs-keyword">type</span> <span class="hljs-title class_">PersonWithoutAddress</span> = <span class="hljs-title class_">Omit</span>&lt;<span class="hljs-title class_">Person</span>, <span class="hljs-string">"address"</span>&gt;;</pre><p>在上面的示例中,<code>Omit&lt;Person, "address"&gt;</code> 返回了一个新类型 <code>PersonWithoutAddress</code>,该类型排除了 <code>Person</code> 接口中的 <code>address</code> 属性。</p><h4><code>Readonly&lt;T&gt;</code></h4><p><code>Readonly&lt;T&gt;</code> 是 TypeScript 中的另一个内置泛型函数,它将类型 <code>T</code> 中的所有属性转换为只读属性。</p><div class="widget-codetool" style="display: none;">
      <div class="widget-codetool--inner">
                  <button type="button" class="btn btn-dark rounded-0 sflex-center copyCode" data-toggle="tooltip" data-placement="top" data-clipboard-text="interface Person {
                    name: string;
                      age: number;
                      }
                      
                      type ReadonlyPerson = Readonly<Person>;" aria-label="复制" data-bs-original-title="复制">
                      <i class="far fa-copy"></i>
          </button>
</div>
      </div><pre class="typescript hljs language-typescript"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">Person</span> {
        <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;
          <span class="hljs-attr">age</span>: <span class="hljs-built_in">number</span>;
          }
          
          <span class="hljs-keyword">type</span> <span class="hljs-title class_">ReadonlyPerson</span> = <span class="hljs-title class_">Readonly</span>&lt;<span class="hljs-title class_">Person</span>&gt;;</pre><p>在上面的示例中,<code>Readonly&lt;Person&gt;</code><code>Person</code> 接口中的所有属性变为只读属性,创建了一个新类型 <code>ReadonlyPerson</code></p><h3 id="item-1-4">总结</h3><p>泛型和类型体操是 TypeScript 中强大的类型系统的关键组成部分。通过使用泛型,我们可以创建可重用、灵活和类型安全的代码。内置泛型函数提供了一些常用的类型转换工具,如 <code>Partial</code><code>Required</code><code>Pick</code>,可以帮助我们更方便地处理类型操作。</p><p>通过结合泛型、<code>extends</code> 关键字、内置泛型函数和其他高级类型概念,我们能够在 TypeScript 中编写更复杂、类型安全的代码,并利用 TypeScript 的强大类型系统来提高代码的可读性、可维护性和可扩展性。</p>