簡體   English   中英

Typescript 泛型包裝器:無類型函數調用可能不接受類型參數

[英]Typescript Generics wrapper: Untyped function calls may not accept type arguments

這個帶有泛型的代碼片段工作得很好( 鏈接到簡單和工作代碼

const state: Record<string, any> = {
    isPending: false,
    results: ['a', 'b', 'c']
}

const useValue = <T extends {}>(name: string): [T, Function] => {
    const value: T = state[name]
    const setValue: Function = (value: T): void => {
        state[name] = value
    }
    return [value, setValue]
}

const [isPending, setIsPending] = useValue<boolean>('isPending')
const [results, setResults] = useValue<string[]>('results')

在這里我可以確定isPending是一個布爾值,而setIsPending接收一個布爾值作為參數。 同樣適用於resultssetResults作為字符串數組。

然后我用另一種方法useStore包裝代碼( 鏈接到擴展損壞代碼

interface UseStore {
    state: Record<string, any>
    useValue: Function
}

const state: Record<string, any> = {
    isPending: false,
    results: ['a', 'b', 'c']
} 

const useStore = (): UseStore => {
    const useValue = <T extends {}>(name: string): [T, Function] => {
        const value: T = state[name]
        const setValue: Function = (value: T): void => {
            state[name] = value
        }
        return [value, setValue]
    }

    return { state, useValue }
}

const { useValue } = useStore()
const [isPending, setIsPending] = useValue<boolean>('isPending')
const [results, setResults] = useValue<string[]>('results')

最后兩行給了我打字稿錯誤: Untyped function calls may not accept type arguments.

我懷疑useStore接口有問題,但由於它的動態特性,我想不出更好的解決方案。

在使用 Generic 類型以獲得正確的類型提示和代碼完成時,如何擺脫錯誤?

由於useValue的類型是Function因此傳入泛型類型參數是沒有意義的。 他們讓誰受益? 運行時沒有得到它們,它們在編譯器時被擦除。 編譯器不能使用它們,因為Function只是一個無類型的函數,所以沒有任何好處。 傳遞類型參數是無用的,並且可以說是一個錯誤(即用戶不希望這是Function ,而是傳入類型參數認為它們有一些效果)。

刪除類型參數並刪除以任何方式輸入的假象:

const { useValue } = useStore()
const [isPending, setIsPending] = useValue('isPending')
const [results, setResults] = useValue('results')

更有趣的問題是你為什么要寫這樣的代碼,因為有一種方法可以完全輸入這段代碼中的所有內容:

const state = {
    isPending: false,
    results: ['a', 'b', 'c']
}
type State = typeof state;

const useStore = () => {
    const useValue = <K extends keyof State>(name: K) => {
        const value = state[name]
        const setValue = (value: State[K]): void => {
            state[name] = value
        }
        return [value, setValue] as const
    }

    return { state, useValue }
}
type UseStore = ReturnType<typeof useStore>;

const { useValue } = useStore()
const [isPending, setIsPending] = useValue('isPending')
const [results, setResults] = useValue('results')

上面的版本是完全類型安全的,不需要任何名稱或類型的重復(您當然可以將其拆分為多個文件,但可能需要根據您的要求進行一些重復)。 如果這不適用於您的情況,我很想知道原因。

編輯

如果您只想在最后幾行計算類型並在那里有一些類型安全,您只需要使用泛型為函數指定簽名:

interface UseStore {
    state: Record<string, any>
    useValue: <T,>(name: string) => [T, (value: T)=> void]
}

const state: Record<string, any> = {
    isPending: false,
    results: ['a', 'b', 'c']
}

const useStore = (): UseStore => {
    const useValue = <T,>(name: string): [T, (value: T)=> void] => {
        const value: T = state[name]
        const setValue = (value: T): void => {
            state[name] = value
        }
        return [value, setValue]
    }

    return { state, useValue }
}

const { useValue } = useStore()
const [isPending, setIsPending] = useValue<boolean>('isPending')
const [results, setResults] = useValue<string[]>('results')

編輯 - 一個開放式的接口實現

您可以將State定義為接口,因為接口是開放式的,您可以在需要時添加成員。 好處是如果其他人定義了一個名稱相同但類型不同的屬性,你會得到一個錯誤

接口狀態{

}
// Don't know what is in here, empty object for starters 
const state : State = {
} as State


const useStore = () => {
    const useValue = <K extends keyof State>(name: K) => {
        const value = state[name]
        const setValue = (value: State[K]): void => {
            state[name] = value
        }
        return [value, setValue] as const
    }

    return { state, useValue }
}
type UseStore = ReturnType<typeof useStore>;

const { useValue } = useStore()

interface State { isPending: boolean }
state.isPending = false; // no guarantee people actually intialize, maybe initial value can be passed to useValue ? 
const [isPending, setIsPending] = useValue('isPending')

interface State { results: string[] }
state.results = ['a', 'b', 'c'];
const [results, setResults] = useValue('results')


interface State { results: number[] } // some else wants to use results for something else, err 
const [results2, setResults2] = useValue('results')

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM