ts 小技巧

51 阅读7分钟

www.typescriptlang.org/docs/handbo…

// infer 的中文是“推断”的意思,一般是搭配上面的泛型条件语句使用的,所谓推断,就是你不用预先指定在泛型列表中,在运行时会自动判断,不过你得先预定义好整体的结构。举个例子
type Foo<T> = T extends { t: infer Test } ? Test : string;

// infer 的中文是“推断”的意思,一般是搭配上面的泛型条件语句使用的,所谓推断,就是你不用预先指定在泛型列表中,在运行时会自动判断,不过你得先预定义好整体的结构。举个例子

// infer用来对满足的泛型类型进行子类型的抽取
type One = Foo<number>; // string,因为number不是一个包含t的对象类型
type Two = Foo<{ t: boolean }>; // boolean,因为泛型参数匹配上了,使用了infer对应的type
type Three = Foo<{ a: number; t: () => void }>; // () => void,泛型定义是参数的子集,同样适配

type Obj = { name: string; age: string };
type R1<T> = keyof T;
const O: R1<Obj> = 'age';

利用 [number] 属性


interface ColorMap {
  first: 'blue';
  secondary: 'red';
  three: 'yellow';
}

type FirstColor = ColorMap['first'];

// type NoFirstColor = ColorMap['first'] | ColorMap['secondary']
type NoFirstColor = ColorMap['first' | 'secondary'];

type EveryColor = ColorMap[keyof ColorMap];

type TupleLetters = ['a', 'b', 'c', 1];
type AB = TupleLetters[0 | 1];
type All = TupleLetters[number];

interface UserRole {
  admin: ['add', 'delete', 'update'];
  user: ['add1', 'delete2'];
}

type Role = UserRole[keyof UserRole][number];

24:

23: 巧用可选属性

type UserRoleType = 'admin' | 'superAdmin'

interface UserType {
  name: string;
  role?: UserRoleType;
}

const createUser = (user: UserType) => {};

createUser({
  name: '小明',
  role: 'admin',
})

createUser({
  name: '小亮'
})

22: as const

const People = [
  'admin',
  'user',
  'titian'
]

People[0] = 'Eddy';

// 利用 as const 声明为字面量
const tsPeople = [
  'admin',
  'user',
  'titian'
] as const

// 不能通过tsPeople[0] 来进行修改,ts 报错
tsPeople[0] = 'Eddy';

// 对象也是如此

const PeopleObj = {
  admin: 'admin',
  user: 'user',
  titian: 'titian'
} as const

PeopleObj.admin= 'test'

21:

export type Obj = {
  a: 'a';
  a2: 'ATwo';
  a3: 'a3';
  b: 'b';
  b1: 'b1';
  b2: 'b2';
};

type ValuesOfKeysStartWithA<T> = {
  [K in Extract<keyof T, `a${string}`>]: T[K];
}[Extract<keyof T, `a${string}`>];

type newUnion = ValuesOfKeysStartWithA<Obj>;

type ValuesOfKeysStartWithAF<T, _ExtractKeys = Extract<keyof T, `a${string}`>> = {
  [K in Extract<keyof T, `a${string}`>]: T[K];
}[Extract<keyof T, `a${string}`>];

type newUnion1 = ValuesOfKeysStartWithAF<Obj>;

type ValuesOfKeysStartWithAB<T, _ExtractKeys extends keyof T = Extract<keyof T, `a${string}`>> = {
  [K in _ExtractKeys]: T[K];
}[_ExtractKeys];

type newUnion2 = ValuesOfKeysStartWithAB<Obj>;


19:

interface Animal {
  name: string;
}

interface Human {
  firstName: string;
  lastName: string;
}

export const getDisplayName = (item: Animal): { displayName: string } => {
  return {
    displayName: item.name,
  };
};

const result = getDisplayName({
  name: 'patch',
});

const result2 = getDisplayName({
  firstName: 'bob',
  lastName: 'alice',
});

export const getDisplayName1 = (item: Animal | Human): { displayName: string } => {
 if ('name' in item) {
  return {
    displayName: item.name,
  };
 }
};

const result4 = getDisplayName1({
  name: 'patch',
});

const result3 = getDisplayName1({
  firstName: 'bob',
  lastName: 'alice',
});


export const getDisplayName2 = <TItem extends Animal | Human>(item: TItem): TItem extends Human ? {firstName: string}: {name:string} => {
  if ('name' in item) {
   return {
    name: item.name,
   };
  }else {
    return {
      firstName: item.firstName
    }
  }
 
 };
 
 const result5 = getDisplayName2({
   name: 'patch',
 });
 
 const result6 = getDisplayName2({
   firstName: 'bob',
   lastName: 'alice',
 });
 


17: omit 实现?

export type Letters = 'jpg' | 'png' | 'svg';

type RemoveSvg<T> = T extends 'svg' ? never : T;

type Omit<T, K> = T extends K ? never : T;

type WithoutSvg = RemoveSvg<Letters>;

type WithoutPng = Omit<Letters, 'png'>;

16:

export type Event =
  | {
      type: 'Log_In';
      payload: {
        userId: string;
      };
    }
  | {
      type: 'sign_out';
    };

const sendEvent = (eventType: Event['type'], payload?: any) => {};

const sendEvent1 = <Type extends Event['type']>(
  ...args: Extract<Event, {type: Type}> extends {payload: infer TPayload} ? [type: Type, payload: TPayload] : [type: Type]
) => {}

sendEvent('sign_out');
sendEvent('Log_In', {
  userId: '1222',
});

sendEvent('sign_out', {});
sendEvent('Log_In', {
  userId: '1222',
});
sendEvent('Log_In', {});
sendEvent('Log_In');

sendEvent1('sign_out',{
})

13

// 如果我们有一个常量文件定义了一些常量,比如 constants 文件中有下面这些
export const ADD_TODO = 'ADD_TODO';
export const REMOVE_TODO = 'REMOVE_TODO';
export const EDIT_TODO = 'EDIT_TODO';

// 但是当我们定义 Action 类型的时候,一般会再手动写一次 type 定义: 如
type ActionType = 'ADD_TODO' | 'REMOVE_TODO' | 'EDIT_TODO';

// 这样的话,当我们常量文件修改的话,对应的 ActionType 也要修改

// 所以我们通过 typeof import('./constants'),将其导出为 ActionModule 类型,typeof 返回的是一个对象类型

export type ActionModule = typeof import('./a');

const actionModule: ActionModule = {
  ADD_TODO: 'ADD_TODO',
  REMOVE_TODO: 'REMOVE_TODO',
  // EDIT_TODO: 'EDIT_TODO'
};

// 利用 keyof 将其转换为 ActionType
export type Action = ActionModule[keyof ActionModule];

12

type Size = 'sm' | 'md' | 'xs' | string;
type SizeTwo = 'sm' | 'md' | 'xs' | Omit<string, 'sm' | 'md' | 'xs'>;
type AutoComplete<T extends string> = T | Omit<string, T> 
type SizeThree = AutoComplete<'sm'| 'md' | 'xs'>

  1. deepPartial
interface User {
  id: string;
  comment: {
    value: string;
  }[];
  meta: {
    name: string;
    desc: string;
  }
}

const UserAlice: Partial<User> = {
  id: 'alice',
  comment: [{value: 'name'}],
  meta: {
    name: 'alice',
    // 如果使用Partial,meta 更深层次的属性并不能设为为可选属性,所以需要 deepPartial
  }
}


export type DeepPartial<T> = T extends Function
  ? T
  : T extends Array<infer InferredArrayMember>
  ? DeepPartialArray<InferredArrayMember>
  : T extends object
  ? DeepPartialObject<T>
  : T | undefined;

interface DeepPartialArray<T> extends Array<DeepPartial<T>> {}

type DeepPartialObject<T> = {
  [K in keyof T]: DeepPartial<T[K]>;
};

10: 将抛出的错误移动到类型提示的级别

// 假设我们有一个参数比较的函数 deepEqualCompare

const deepEqualCompare = <T,>(a: T, b: T): boolean => {
  if (Array.isArray(a) || Array.isArray(b)) {
    throw new Error('you cannot compare two arrays');
  }
  return a === b;
};

deepEqualCompare(1, 1);

// 当我们对比数组的时候会抛出错误,我们可以利用 ts 将错误提升到类型级别
deepEqualCompare([], []);

type CheckIsArray<T> = T extends any[] ? 'you cannot compare two arrays' : T;

const deepEqualCompare1 = <T,>(
  a: CheckIsArray<T>,
  b: CheckIsArray<T>,
): boolean => {
  if (Array.isArray(a) || Array.isArray(b)) {
    throw new Error('you cannot compare two arrays');
  }
  return a === b;
};

deepEqualCompare1([], []);

9:

const makeKeyRemove = <Key extends string>(keys: Key[]) => {
  return <Obj,>(obj: Obj): Omit<Obj, Key> => {
    return {} as any;
  };
};

const keyRemover = makeKeyRemove(['a', 'b']);

const newObject = keyRemover({ a: 1, b: 2, c: 3 });
interface TableProps {
  items: { id: string }[];
  renderItem: (item: { id: string }) => React.ReactNode;
}

export const Table = (props: TableProps) => {
  return null;
};

// 有时候,想要在 items 中传递任意属性,而不是只传递 id
const Component = () => {
  return (
    <Table
      items={[{ id: '1', age: 'name' }]}
      renderItem={(item) => <div>{item.id}</div>}
    ></Table>
  );
};

interface TableProps1<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

export const Table1 = <T,>(props: TableProps1<T>) => {
  return null;
};

const Component1 = () => {
  return (
    <Table1<{ id: number; age: string }>
      items={[{ id: 1, age: 'name' }]}
      renderItem={(item) => <div>{item.id}</div>}
    ></Table1>
  );
};


export const myObject = {
  a: 1,
  b: 2,
  c: 3,
};

Object.keys(myObject).forEach((key) => {
  console.log(myObject[key as keyof typeof myObject]);
});

// 方法二

const objectKeys = <T extends object>(obj: T): (keyof T)[] => {
  return Object.keys(obj) as (keyof T)[];
};

objectKeys(myObject).forEach((key) => {
  console.log(myObject[key]);
});

const MyComponent = (props: { enabled: boolean }) => {
  return null;
};

class MyOtherComponent extends React.Component<{
  enabled: boolean;
}> {}

type PropsFrom<TComponent> = TComponent extends React.FC<infer Props>
  ? Props
  : TComponent extends React.Component<infer Props>
  ? Props
  : never;

const props: PropsFrom<MyOtherComponent> = {
  enabled: true,
};

export const getDeepValue = <Obj, FirstKey, SecondKey>(
  obj: Obj,
  firstKey: FirstKey,
  secondKey: SecondKey,
) => {
  return {} as any;
};

const obj = {
  foo: {
    a: true,
    b: 2,
  },
  bar: {
    c: 'cool',
    d: 2,
  },
};

const result = getDeepValue(obj, 'bar', 'd');

export const getDeepValue1 = <
  Obj,
  FirstKey extends keyof Obj,
  SecondKey extends keyof Obj[FirstKey],
>(
  obj: Obj,
  firstKey: FirstKey,
  secondKey: SecondKey,
): Obj[FirstKey][SecondKey] => {
  return obj[firstKey][secondKey] as any;
};

const result1 = getDeepValue1(obj, 'foo', 'a');
  1. 函数重载
// 函数重载

export function compose<Input, FirstArg>(
  func: (input: Input) => FirstArg,
): (input: Input) => FirstArg;

export function compose<Input, FirstArg, SecondArg>(
  func: (input: Input) => FirstArg,
  func2: (input: FirstArg) => SecondArg,
): (input: Input) => SecondArg;

export function compose<Input, FirstArg, SecondArg, ThirdArg>(
  func: (input: Input) => FirstArg,
  func2: (input: FirstArg) => SecondArg,
  func3: (input: SecondArg) => ThirdArg,
): (input: Input) => ThirdArg;

export function compose(...args: any[]) {
  return {} as any;
}

const addOne = (a: number) => {
  return a + 1;
};

const numToString = (a: number) => {
  return a.toString();
};

const stringToNum = (a: string) => {
  return parseInt(a);
};

const addOneToString = compose(addOne);
const addTwoToString = compose(addOne, numToString);
const addThreeToString = compose(addOne, numToString, stringToNum);
import { String } from 'ts-toolbelt';

const query = '/home?a=foo&b=wow';

type Query = typeof query;

type SecondQueryPart = String.Split<Query, '?'>[1];

type QueryElements = String.Split<SecondQueryPart, '&'>;

type QueryParams = {
  [QueryElement in QueryElements[number]]: {
    [Key in String.Split<QueryElement, '='>[0]]: String.Split<
      QueryElement,
      '='
    >[1];
  };
}[QueryElements[number]];

const obj: Union.Merge<QueryParams> = {
  a: 'foo',
  b: 'woo',
};

export type Entity = { type: 'user' } | { type: 'post' } | { type: 'comment' };
type s = keyof Entity;

// 动态添加type的id
type EntityWithId =
  | { type: 'user'; userId: string }
  | { type: 'post'; postId: string }
  | { type: 'comment'; commentId: string };

type EntityWithId1 = {
  [EntityType in Entity['type']]: {
    type: EntityType;
  } & Record<`${EntityType}Id`, string>;
}[Entity['type']];
const entityResult1: EntityWithId1 = {
  type: 'user',
  userId: 'ss',
};

const entityResult: EntityWithId = {
  type: 'user',
  userId: '124',
};

  1. 如何将对象改为联合类型
export const fruitCounts = {
  apple: 1,
  pear: 4,
  banana: 26,
};

type SingleFruitCountType =
  | { apple: number }
  | { banana: number }
  | { pear: number };

const SingleFruitCount: SingleFruitCountType = {
  banana: 12,
};

// 修改重构 SingleFruitCountType 这段类型定义代码

type fruitCountsType = typeof fruitCounts;

type newSingleFruitCount = {
  [K in keyof fruitCountsType]: {
    [K2 in K]: number;
  };
}[keyof fruitCountsType];

const SingleFruitCount1: newSingleFruitCount = {
  banana: 12,
};

// 联合类型转交叉类型

type UnionToIntersection<U> = (U extends U ? (a: U) => any : never) extends (
  a: infer R,
) => any
  ? R
  : never;

type res = UnionToIntersection<{ a: 1 } | { b: 2 }>;

type Copy<Obj> = {
  [K in keyof Obj]: Obj[K];
};

type AOrB = keyof res;

type res2 = Copy<UnionToIntersection<{ a: 1 } | { b: 2 }>>;

请教一下 x6 采用自定义节点 绘制 现在要进行 导出png/svg 有大佬处理过吗

用哪个自带的那个方法,导出样式和节点图片会缺失

样式写行内 图片用base64

codeShonwBox

定义联合类型:

function printId(id: number | string) {
	console.log('ID is' + id)
}

提供与联合类型匹配的值非常容易,只需要提供一个值,这个值只要与联合类型中国的任意一个成员匹配即可,但是如果有一个联合类型的值,在做操作的时候,这个操作必须对联合类型中的每一个成员都有效

function printId(id: number | string) {
	console.log(id.toUpperCase())
	// ts 提示错误 toUpperCase 不能用于number
}

解决办法就是缩小类型

function printId(id: number | string) {
	if (typeof if === 'string') {
		console.log(id.toUpperCase())
	} else {
		console.log(id)
	}
}

const stringToNum = (a: string) => { return parseInt(a); };

const addOneToString = compose(addOne); const addTwoToString = compose(addOne, numToString); const addThreeToString = compose(addOne, numToString, stringToNum);

3.

```ts
import { String } from 'ts-toolbelt';

const query = '/home?a=foo&b=wow';

type Query = typeof query;

type SecondQueryPart = String.Split<Query, '?'>[1];

type QueryElements = String.Split<SecondQueryPart, '&'>;

type QueryParams = {
  [QueryElement in QueryElements[number]]: {
    [Key in String.Split<QueryElement, '='>[0]]: String.Split<
      QueryElement,
      '='
    >[1];
  };
}[QueryElements[number]];

const obj: Union.Merge<QueryParams> = {
  a: 'foo',
  b: 'woo',
};

const addOneToString = compose(addOne); const addTwoToString = compose(addOne, numToString); const addThreeToString = compose(addOne, numToString, stringToNum);

3.

```ts
import { String } from 'ts-toolbelt';

const query = '/home?a=foo&b=wow';

type Query = typeof query;

type SecondQueryPart = String.Split<Query, '?'>[1];

type QueryElements = String.Split<SecondQueryPart, '&'>;

type QueryParams = {
  [QueryElement in QueryElements[number]]: {
    [Key in String.Split<QueryElement, '='>[0]]: String.Split<
      QueryElement,
      '='
    >[1];
  };
}[QueryElements[number]];

const obj: Union.Merge<QueryParams> = {
  a: 'foo',
  b: 'woo',
};