The big problem you're running into here is that TypeScript does not currently support partial type parameter inference as requested in microsoft/TypeScript#26242.  With a generic function like useSortBy<T, K>({/*...*/}) you can either manually specify both T and K type parameters, or the compiler can try to infer both type T and K type parameters.  There's no direct way to define it so that T must be manually specified but that K should be inferred.
The workaround I tend to use in these cases is currying; make useSortBy a function generic in only T, and have it return another function generic in only K.  This changes the call from the desired useSortBy<T>({/*...*/}) to the slightly weird useSortBy<T>()({/*...*/}), but it works.
The second problem you're running into is that the compiler is using the defaultSortOption parameter to infer K instead of the sortOptions parameter.  So you only get "nameDesc" instead of "nameAsc" | "nameDesc".  What you want to tell the compiler is that the K in the type of defaultSortOption should be a non-inferential use of the type parameter; it should infer K from sortOptions alone, and then just check defaultSortOption with it.  Such non-inferential type parameter usage is requested in microsoft/TypeScript#14829, but there is no direct support for this.  Again, though, there are workarounds.
One workaround you can use is to introduce a second type parameter (let's call it L) which is constrained to the first one.  You can use this new type parameter instead of K anywhere you'd like to be non-inferential.  Then the compiler will infer K only from the places you want (because those are the only places mentioning K at all), and whatever it infers for L will be checked against K.
Let's use both of these workarounds in your useSortBy example.  For clarity I'm introducing the following types
interface Model { name: string, age: number }
type SortOption<T> = {
  fn: (list: T[]) => T[];
  label: string;
}
Now here is the curried and extra-type-parametered useSortBy():
const useSortBy = <T,>() => <K extends string, L extends K>(opts: {
  sortOptions: Record<K, SortOption<T>>;
  defaultSortOption: L;
}) => { }
Let's use it:
useSortBy<Model>()({
  sortOptions: {
    nameAsc: {
      fn: (list) => list.slice().sort((a, b) => a.name.localeCompare(b.name)),
      label: "Name A-Z",
    },
    nameDesc: {
      fn: (list) => list.slice().sort((a, b) => b.name.localeCompare(a.name)),
      label: "Name Z-A",
    }
  },
  defaultSortOption: "nameDesc",
});
That works fine and note that the list callback parameters are contextually types to be Model[] without needing explicit type annotations.  And if you put a value for defaultSortOption that does not exist as a key of the sortOptions property, you see the desired error in the desired place:
useSortBy<Model>()({
  sortOptions: {
    nameAsc: {
      fn: (list) => list.slice().sort((a, b) => a.name.localeCompare(b.name)),
      label: "Name A-Z",
    },
    nameDesc: {
      fn: (list) => list.slice().sort((a, b) => b.name.localeCompare(a.name)),
      label: "Name Z-A",
    }
  },
  defaultSortOption: "oopsieDoodle", // error!
  //~~~~~~~~~~~~~~~ <-- Type '"oopsieDoodle"' is 
  // not assignable to type '"nameAsc" | "nameDesc"'
});
Playground link to code