Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
399 views
in Technique[技术] by (71.8m points)

typescript多参数的返回值类型确认问题

直接上代码

class Store<T> {
    state: T
    constructor(state: T) {
        this.state = state
    }
    get(): T {
        return this.state
    }
}

function useStore(...arg) {
    const stores = arg.slice(0, -1)
    const fn = arg.slice(-1)[0]
    const state = {}

    stores.forEach(([store, getState]) => Object.assign(state, getState(store.get())))

    fn(state)
}

const s1 = new Store({ a: 1 }) 
const s2 = new Store({ b: 2 })

useStore(
    [s1, state => ({
        a: state.a
    })],
    [s2, state => ({
        c: state.b
    })],
    ...可能有更多,但是格式一致
    
    function (props) {
        console.log(props)
    }
)

在这里我期望的是,在使用的时候

[s1, state => ({
    a: state.a // <- 期望这里能够实现字段的自动提示
})],

function (props) {
    // 以及props也能实现字段的自动提示
    console.log(props)
}

我也试过

type Cb<T, U> = (state: T) => U

在单个参数的时候是可行的,多个参数的时候就行不通了,ts初学,多参数多回调多类型确实搞不定了,如果有会的希望解答一下,就算改变用法也没关系


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

这个和 rxjs 的 pipe 有异曲同工之处,参数无限多,每个参数还会影响后续类型推导,这个要做类型推导是无解的,但是有权衡解法。目前只能把简单情况枚举出来,先人肉列出足够多的参数覆盖大部分使用情况,参数再多就 fallback 到丢失类型推导的下策方案。这里当然用到的函数声明类型重载。

实现效果:

线上 typescript playground 版本

第一步 先定义一个辅助类型

useStore 除末尾参数外,每个参数其实都是一个二元组,第一个元素是 Store<T>,第二个是一个接受 T 返回新类型 U 的函数。

type S<T, U> = [Store<T>, (t:T) => U];
第二步 从一个 store 一个 fn 的情况开始,逐步列出更多参数的情况

最终列出几个看你自己的需求喽。 一个 store 长这样,记得尾部分号,因为是定义函数重载,函数体在后面。

function useStore<T1, U1>(s1: S<T1, U1>, fn: (a: U1) => any): void;

两个长这样:

function useStore<T1, U1, T2, U2>(
    s1: S<T1, U1>, 
    s2: S<T2, U2>, 
    fn: (a: U1 & U2) => any
): void;`

三个

function useStore<T1, U1, T2, U2, T3, U3>(
    s1: S<T1, U1>, 
    s2: S<T2, U2>, 
    s3: S<T3, U3>, 
    fn: (a: U1 & U2 & U3) => any
): void;

最后兜底,防止参数超过定义数量 ts 报错。由于没法定义元素最后一个参数为特定类型,只能这样将就了。或者不定义兜底也行,相当于在 ts 层面不支持无限参数,反正也是 any 乱飞。

function useStore(...args: (S<any, any> | ((a: any) => any))[]): void;

合起来:

class Store<T> {
  state: T
  constructor(state: T) {
    this.state = state
  }
  get(): T {
    return this.state
  }
}

type S<T, U> = [Store<T>, (t:T) => U];

function useStore<T1, U1>(s1: S<T1, U1>, fn: (a: U1) => any): void;
function useStore<T1, U1, T2, U2>(s1: S<T1, U1>, s2: S<T2, U2>, fn: (a: U1 & U2) => any): void;
function useStore<T1, U1, T2, U2, T3, U3>(s1: S<T1, U1>, s2: S<T2, U2>, s3: S<T3, U3>, fn: (a: U1 & U2 & U3) => any): void;
function useStore(...args: (S<any, any> | ((a: any) => any))[]): void;
function useStore(...args: any[]) {
  const stores = args.slice(0, -1) as S<any, any>[];
  const fn = args.slice(-1)[0] as (a: any) => any;
  const state: any = {};

  stores.forEach(([store, getState]) => Object.assign(state, getState(store.get())));

  fn(state);
}

const s1 = new Store({ a: 1 }) 
const s2 = new Store({ b: 2 })

useStore(
  [s1, state => ({
    a: state.a
  })],
  [s2, state => ({
    b: state.b
  })],
  [s1, state => ({
    c: String(state.a * state.a)
  })],
  // [s2, state => ({
  //     d: state.b * state.b
  // })],
  function (props) {
    type C = typeof props.c;
    props.b;
  }
);

useStore(
  [s1, state => ({
    test: state.a
  })],
  props => console.log(props.test)
);

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share

2.1m questions

2.1m answers

63 comments

56.6k users

...